Method Clashing With A Field Name
ID |
java.method_name_matches_field |
Severity |
critical |
Remediation Complexity |
medium |
Remediation Risk |
low |
Remediation Effort |
medium |
Resource |
Code Smell |
Language |
Java |
Tags |
code-style, naming |
Description
Reports a method whose name is the same as a field of the same type, or differs from it only in capitalization.
Fields and methods declared directly in the type are compared; record components count as fields. Inherited members are not considered.
Capitalization is folded for ordinary fields only. A constant is compared by its exact name: SCREAMING_SNAKE for constants and camelCase for methods are two conventions being followed, so static Version zero() returning static final Version ZERO is not a clash.
Rationale
The pair is read wrong by everyone who comes after. At the call site ferry.route and ferry.route() look like the same thing, and a Route / route pair looks like a typo that the compiler happily accepts. A method name is supposed to name an action and therefore contain a verb, which a field name almost never does — so the clash is usually a naming slip rather than a deliberate design.
public class Ferry {
public String route;
private int berth;
public void route() { // clashes with the field
...
}
public void Berth(int slot) { // differs from the 'berth' field only in case
...
}
}
The following are intentional idioms and are not reported. All of them require the method name to match the field name exactly — a pair that differs in capitalization is always reported, because two spellings of one name is the defect itself.
-
an accessor — a zero-argument, non-void method whose name is exactly the field name (
name()returningname); -
a mutator of the same field, whatever it returns — the builder idiom. The return type is no guide to it: a nested builder returns
Outer.Builder, a generic one returns its self-typeT, and plenty of mutators are plainvoid. What is checked is that the method takes a parameter and its body writes that field, however it gets there: after a guard, from inside aswitch, alongside a companion field, through the object the field holds (this.enabled.set(v)against aBooleanProperty), or by delegating to another overload of the same name (ids(String… v) { return ids(List.of(v)); }); -
a
staticmethod clashing with an instance field. The two are never reached through the same expression — a call site readsCellSpec.text(…)orspec.text, never both — and such a pair is almost always the named static factory of a value type, named after the field it fills in. Astaticmethod clashing with astaticfield is reported; -
a method annotated
@Override, because its name comes from the supertype; -
a pair where both members are private, which cannot confuse anyone outside the type.
public final class Options {
private final BooleanProperty verbose = new SimpleBooleanProperty();
private List<String> tags;
public void verbose(boolean verbose) { // not reported: writes through the property
this.verbose.set(verbose);
}
public Options tags(List<String> tags) { // not reported: a guard before the write
if (tags.isEmpty()) {
throw new IllegalArgumentException("empty");
}
this.tags = List.copyOf(tags);
return this;
}
public Options tags(String... tags) { // not reported: delegates to the overload above
return tags(List.of(tags));
}
}
A zero-argument action that flips a same-named flag is not a mutator and is still reported — void shutdown() setting shutdown = true stores nothing the caller passed, and obj.shutdown versus obj.shutdown() is exactly the pair a reader trips over.
Remediation
Rename the member. Renaming a public method can break callers, so prefer renaming the field — and while you are there, give the method a verb.
public class Ferry {
public String routeName;
private int berthNumber;
public void sail() {
...
}
public void assignBerth(int slot) {
...
}
}
If the method really is an accessor or a mutator for the field, give it the exact field name — a zero-argument accessor, or a mutator that takes the value and writes that field — and the pair is accepted as the getter / builder idiom.