“Return an immutable interface to the original data. You can then change fields in the object, but the caller cannot unless he cheats by casting. You expose only the methods you want the user to have. Doing the same with classes is trickier since a subclass must expose everything its superclass does”
What does he mean you can cheat, and why is it tricky with subclasses ?
You provide an
interfacethat has no mutation methods. Then, you provide mutable implementations that are only known to the creator.At this point, if you return
Personobjects everywhere, the only way for someone to modify the returned object is to cheat and cast it back to aMutablePerson. In effect, the mutable objects become immutable unless the code is a complete hack.When not dealing with a bunch of younger programmers that will notice the true implementation is mutable, and thus they can cast it to change it, then you provide the safety of immutability with the benefit of being able to put it together without an endless constructor, or using a Builder (in effect, the mutable version is the Builder). A good way to avoid developers abusing the mutable version is to leave the mutable version as package private so that only the package knows about it. The negative of that idea is that this only works if it will be instantiated in the same package, which may be the case, but it obviously may not be the case in situations such as where DAO’s are used with multiple package-defined implementations (e.g., MySQL, Oracle, Hibernate, Cassandra, etc., all returning the same stuff, and hopefully separated from each other to avoid cluttering their packages).
The real key here is that people should never build up from the Mutable objects except to implement further-down interfaces. If you’re extending, and then returning an immutable subclass, then it’s not immutable if it exposes a mutable object, by definition. For example:
This is honestly the way that
Collections in Java should have been implemented. A readonly interface could have taken the place of theCollections.unmodifiableimplementations, thus not having people unexpectedly using immutable versions of mutable objects. In other words, you should never hide immutability, but you can hide mutability.Then, they could sprinkle immutable instances that truly can’t be modified, and that would keep developers honest. Similarly, I would likely expect to see an immutable version of the above interface somewhere (with better names):