Summary: Record accessors should not be used inside compact constructors because they read uninitialized fields.
In a Java record, using the accessor method (like d()) inside a compact
constructor (the one without arguments, Foo { ... }) reads the record’s
underlying field before it has been set. This means the method will always
return null (for objects) or 0/false (for primitives), regardless of what
arguments were passed to the constructor.
record User(String name) {
User {
// BUG: name() reads the uninitialized field 'this.name', which is currently
// null. This throws a NullPointerException immediately.
if (name().isEmpty()) {
throw new IllegalArgumentException("Name cannot be empty");
}
}
}
record User(String name) {
User {
// CORRECT: Reads the constructor parameter 'name'.
if (name.isEmpty()) {
throw new IllegalArgumentException("Name cannot be empty");
}
}
}
The Compact Constructor in Java is a special initialization block that runs before the record’s fields are automatically assigned.
User { ... }
runs first.this.name = name;) only after your code block finishes
(JLS §8.10.4.2).When you call name(), it attempts to read this.name. Since the assignment
hasn’t happened yet, this.name still holds its default value, which is null
(JLS §4.12.5).
To fix this, refer to the component by its name (e.g., name). This accesses
the parameter passed to the constructor, which holds the correct value.
Suppress false positives by adding the suppression annotation
@SuppressWarnings("RecordAccessorInCompactConstructor") to the enclosing code.
Suppress false positives by adding the suppression annotation @SuppressWarnings("RecordAccessorInCompactConstructor") to the enclosing element.