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
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?
In Java 8 and earlier versions, we can use collection class utility methods like unmodifiableXXX to create immutable collection objects. If we need to create an immutable list then use the Collections. unmodifiableList() method.
Records are immutable data classes that require only the type and name of fields. The equals, hashCode, and toString methods, as well as the private, final fields and public constructor, are generated by the Java compiler.
About Immutability. The collections returned by the convenience factory methods added in JDK 9 are conventionally immutable. Any attempt to add, set, or remove elements from these collections causes an UnsupportedOperationException to be thrown.
From Java 9, static factory methods are introduced to create immutable collections. 1) Immutable List : List.of () 2) Immutable Set : Set.of () 3) Immutable Map : Map.of () or Map.ofEntries ()
1) Immutable List : List.of () 2) Immutable Set : Set.of () 3) Immutable Map : Map.of () or Map.ofEntries () Java 9 Immutable collections and unmodifiable collections returned by the Collections.unmodifiableXXX () wrapper methods are not the same.
Immutable collections can't be changed at all - they don't wrap another collection - they have their own elements. Unlike Collections.unmodifiableList (java.util.List<? extends T>), which is a view of a separate collection that can still change, an instance of ImmutableList contains its own private data and will never change.
Unmodifiable collections are just the read-only views of the original collection. You can perform modifying operations on the original collection and those modifications will be reflected in the collections returned by these methods. But, immutable collections returned by Java 9 static factory methods are 100% immutable.
You can do it already, the arguments of the constructor are mutable:
record SomeRecord(Set<Thing> set) {
public SomeRecord {
set = Set.copyOf(set);
}
}
A related discussion mentions the argument are not final in order to allow such defensive copying. It is still the responsibility of the developer to ensure the rule on equals()
is held when doing such copying.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With