Is there any way to do Null Objects with Java Records? With classes I'd do it like that:
public class Id {
public static final Id NULL_ID = new Id();
private String id;
public Id(String id) {
this.id = Objects.requireNonNull(id);
}
private Id() {}
}
But that does not work, because every constructor needs go through the canonical (Id(String id
) one and I can't just call super()
to go around the invariants.
public record Id(String id) {
public static final Id NULL_ID = null; // how?
public Id {
Objects.requireNonNull(id);
// ...
}
}
Right now I work around this with
public Id {
if (NULL_OBJECT != null)
Objects.requireNonNull(id);
}
but that feels wrong and open to concurrency problems.
I haven't found a lot of discussion about the design ideas behind records and this may have been already discussed. If it's like that to keep it simple that's understandable, but it feels awkward and I've hit that problem multiple times already in small samples.
No, what you want is not possible with the current definition of records in Java 14. Every record type has a single canonical constructor, either defined implicitly or explicitly. Every non-canonical constructor has to start with an invocation of another constructor of this record type. This basically means, that a call to any other constructor definitely results in a call to the canonical constructor. [8.10.4 Record Constructor Declarations in Java 14]
If this canonical constructor does the argument validation (which it should, because it's public), your options are limited. Either you follow one of the suggestions/workarounds already mentioned or you only allow your users to access the API through an interface. If you choose this last approach, you have to remove the argument validation from the record type and put it in the interface, like so:
public interface Id {
Id NULL_ID = new IdImpl(null);
String id();
static Id newIdFrom(String id) {
Objects.requireNonNull(id);
return new IdImpl(id);
}
}
record IdImpl(String id) implements Id {}
I don't know your use case, so that might not be an option for you. But again, what you want is not possible right now.
Regarding Java 15, I could only find the JavaDoc for Records in Java 15, which seems to not have changed. I couldn't find the actual specification, the link to it in the JavaDoc leads to a 404, so maybe they have already relaxed the rules, because some people complained about them.