Too Much State In One Type

ID

java.max_fields

Severity

high

Remediation Complexity

medium

Remediation Risk

low

Remediation Effort

medium

Resource

Complexity

Language

Java

Tags

complexity

Description

Reports types that declare more state than the configured limit allows. Every variable of a field declaration counts, so int a, b, c; counts as three; record components count as well, because they are the record’s state.

Constants (static final fields), interface and annotation-type fields, enum constants and inherited fields are not counted. Nested and local types are measured on their own members, independently of the type that contains them.

Types whose field list is a table or document mapping — @Entity, @Table, @MappedSuperclass, @Embeddable, MyBatis-Plus @TableName, Spring Data @Document — are skipped entirely. There the advice is not merely unhelpful but impossible to follow: fifty fields are fifty columns, and grouping them into collaborating types changes the schema.

Not every such mapping is written in the file. A MyBatis Mapper.xml, or an Elasticsearch index descriptor, maps a plain class with no annotation on it at all. Those are skipped too, on the two things that *are visible: the type is named as a row type (a name ending in one of rowTypeSuffixes) and its body exposes its state as a column list, declaring at least one field accessor per field. A configuration bean with a hundred fields and no accessors does not qualify, and neither does a …Entity that carries logic of its own.

A Builder nested in the type it builds is skipped as well: its fields mirror the built type’s, which is measured on its own, so reporting both would report one piece of state twice.

Rationale

A long field list is the most reliable outward sign that a type has taken on several responsibilities. Every field is one more thing that constructors have to initialise, that invariants have to keep consistent, that equals, hashCode and serialization have to cover, and that every test has to set up before it can assert anything.

public class Customer {
  private String firstName;
  private String lastName;
  private String street;
  private String city;
  private String postalCode;
  private String country;
  private String cardNumber;
  private String cardHolder;
  private int cardExpiryMonth;
  private int cardExpiryYear;
  // ... and twenty more
}

The field names give the split away: the address fields belong together, and so do the card fields.

Remediation

Look for clusters of fields that always change together or are always read together, and give each cluster its own type. The container keeps one field per cluster instead of five, and each extracted type gets its own focused invariants and tests.

public class Customer {
  private final PersonName name;
  private final PostalAddress address;
  private final PaymentCard card;
}

A type whose state genuinely is a flat list of independent values — a generated data model, or a constants holder — is better handled by raising maxFields for the project than by an artificial split.

Configuration

Property Default Description

maxFields

20

A type whose counted field total is strictly greater than this value is flagged.

rowTypeSuffixes

Dto, DTO, Entity, Row, Document

Name endings of externally mapped row and document types. A type is exempted only when its name ends with one of these and it declares at least one field accessor per field. Empty the list to report those types too.

© 2026 Xygeni. All rights reserved