Overriding the accessor methods like in your first variant violates the expectation that you can create an equal object using the accessor methods and the canonical constructor. From the documentation
For all record classes, the following invariant must hold: if a record R's components are c1, c2, ... cn
, then if a record instance is copied as follows:
R copy = new R(r.c1(), r.c2(), ..., r.cn());
then it must be the case that r.equals(copy)
.
But with your overridden accessor method, the following assertion fails:
MyRecord r1 = new MyRecord(null, null), r2 = new MyRecord(r1.strings(), r1.required());
assert r1.equals(r2);
because the internal fields contain different data.
So the only correct way to fix input data is during the construction, e.g.
public record MyRecord(Set<String> strings, Boolean required) {
public MyRecord {
if(strings == null) strings = Set.of();
if(required == null) required = true;
}
}
However, you shouldn’t do this null
handling at all. Collections should never be null
and using Boolean
for the constructor implies having a Boolean
record component type in general, i.e. also returned by the accessor method. And writing new MyRecord(null, null)
instead of new MyRecord(Set.of(), true)
doesn’t even save much typing.
If you want to support default values, you should overload the constructor, e.g.
public record MyRecord(Set<String> strings, boolean required) {
public MyRecord {
strings = Set.copyOf(strings); // enforce non-null immutable set
}
public MyRecord() {
this(Set.of(), true);
}
}
So you can use new MyRecord()
for the defaults. Or you consider that records are immutable, so constructing multiple instances of defaults isn’t necessary
public record MyRecord(Set<String> strings, boolean required) {
public static final MyRecord DEFAULT = new MyRecord(Set.of(), true);
public MyRecord {
strings = Set.copyOf(strings); // enforce non-null immutable set
}
}
and use MyRecord.DEFAULT
whenever you need the default values. Of course, you still can provide overloaded constructors for the cases that only one parameter should have default values, if that is needed.
method(param1 = "bla"
), this would eliminate even more use cases for Lombok, like in Kotlin or Scala – Cephalothis(...)
calls the generated constructor. For instance;public MyRecord() { this(Set.of(), true); }
– Kisangani@DefaultValue
is another option. – Territorial