Java records are used to implement shallowly immutable data carrier types. If the constructor accepts mutable types then we should implement explicit defensive copying to enforce immutability. e.g.
record Data(Set<String> set) {
public Data(Set<Thing> set) {
this.set = Set.copyOf(set);
}
}
This is mildly annoying - we have to
- implement an old-school POJO constructor (replicating the fields) rather than using the canonical constructor and
- explicitly initialise every field just to handle the defensive copy of the mutable field(s).
Ideally what we want to express is the following:
record SomeRecord(ImmutableSet<Thing> set) {
}
or
record SomeRecord(Set<Thing> set) {
public SomeRecord {
if(set.isMutable()) throw new IllegalArgumentException(...);
}
}
Here we use a fictitious ImmutableSet
type and Set::isMutable
method, in either case the record is created using the canonical constructor - nice. Unfortunately it doesn't exist!
As far as I can tell the built-in collection types (introduced in Java 10) are hidden, i.e. there is no way to determine if a collection is immutable or not (short of trying to modify it).
We could use Guava but that seems overkill when 99% of the functionality is already in the core libraries. Alternatively there are Maven plug-ins that can test classes annotated as immutable, but again that's really a band-aid than a solution.
Is there any pure-Java mechanism to enforce a immutable collection?