This rule raises an issue when a record component accessor is called on the record being constructed, inside a compact constructor or inside an explicit canonical constructor before the corresponding field is assigned.
The fields of a record are assigned only once, by its canonical constructor:
this.component = … statement of the constructor body.Until a field is assigned, it holds the default value of its type: null, 0, or false. Reading such a field
through this. is rejected by the compiler, but calling the component accessor, for example name() or
this.name(), compiles and silently reads the field.
As a consequence, the accessor returns the default value instead of the value passed to the constructor. This typically defeats the validation
performed in the constructor: a check such as name().isBlank() throws a NullPointerException instead of validating the
argument, and a check such as age() < 0 is always false, so invalid values are accepted. The same applies to explicitly
declared accessors, as they typically read the same unassigned field.
Calls placed in lambdas and in local or anonymous classes are not reported, as they may run after the construction of the record.
Read the constructor parameter instead of calling the accessor. In a compact constructor, the parameters have the same names as the record components. In an explicit canonical constructor, either read the parameter or call the accessor only after the field has been assigned.
record User(String name, int age) {
User {
if (name().isBlank()) { // Noncompliant: name() returns null here
throw new IllegalArgumentException("Name must not be blank");
}
if (age() < 0) { // Noncompliant: age() always returns 0 here
throw new IllegalArgumentException("Age must not be negative");
}
}
}
record User(String name, int age) {
User {
if (name.isBlank()) {
throw new IllegalArgumentException("Name must not be blank");
}
if (age < 0) {
throw new IllegalArgumentException("Age must not be negative");
}
}
}
record Email(String address) {
Email(String address) {
if (!address().contains("@")) { // Noncompliant: address() returns null here
throw new IllegalArgumentException("Invalid email address");
}
this.address = address.toLowerCase(Locale.ROOT);
}
}
record Email(String address) {
Email(String address) {
if (!address.contains("@")) {
throw new IllegalArgumentException("Invalid email address");
}
this.address = address.toLowerCase(Locale.ROOT);
}
}