Accessor Uses The Wrong Field
ID |
java.accessor_uses_expected_field |
Severity |
high |
Remediation Complexity |
medium |
Remediation Risk |
low |
Remediation Effort |
medium |
Resource |
Code Smell |
Language |
Java |
Tags |
code_smell, copy-paste, field |
Description
Reports a getter (getX/isX, no parameters, non-void) whose single return statement does
not reference the field named x, or a setter (setX, one parameter, void) whose single
assignment statement does not write the field named x — while a field named x, of the
accessor’s own type, exists in the type.
Rationale
A pair of accessors is often written by copying the first one and renaming it, and the field reference inside the body is left behind. The code compiles and type-checks: the method returns, or assigns, a real field of the right type, so nothing about it looks broken.
public class Money {
private long cents;
private String currency;
public String getCurrency() {
return currency;
}
public long getCents() {
return currency.length(); // wrong field: 'cents' is never read
}
public void setCurrency(String value) {
this.cents = 0; // wrong field: 'currency' is never written
}
}
Several conditions keep the rule quiet on legitimate code:
-
a field named after the accessor must actually exist — an accessor for a name with no matching field is a computed property, not a mistake;
-
the body must be exactly one statement. A longer body may validate, convert or derive the value without directly touching the field, and telling that apart from a real mistake needs more than syntax, so it is left alone rather than guessed at. This is narrower than a full-body scan: a copy-paste mistake hiding inside a multi-statement accessor is not reported by this rule;
-
the field must hold what the accessor returns or takes (compared by simple type name, with a primitive and its wrapper counting as one type).
boolean isEmpty()beside a sentinelNode empty, orJid getService()beside anXmppConnectionService service, is not a mistaken accessor — returning that field would not even compile. A getter that widens (List<T> getChildren()over anArrayList<T> children) is left alone as a consequence; -
a
staticaccessor is never asked to read an instance field; -
a getter that produces the field’s value is a factory, not an accessor. When the field’s own initializer, or an assignment to it anywhere in the type, calls the getter, the data flows field ← method and nothing is wrong;
-
the field may be read one hop away — through another method of the same type (the JavaFX
xProperty().get()convention, an overload, a one-line conversion), or through a second field that is initialized from it (a cachedCollections.unmodifiableList(children)view).
When both a boolean isX field and an unrelated X x field exist, the exactly-named one wins:
boolean isStaff() { return this.isStaff; } is a correct accessor even next to a Staff staff
association.
The expected field name is derived from the accessor name by removing the get/is/set
prefix and folding case on the first character only: getURL expects a field named URL,
not url — every character after the first must match exactly. A field spelled differently
(a leading underscore, an all-lowercase acronym) is not recognised as corresponding to the
accessor and the accessor is left alone.