Value Objects and Immutability
Why make value objects immutable, and how to do it when working in a language like Java or C#?
Why Immutable?
In Effective Java, Joshua Bloch makes this compelling recommendation:
Classes should be immutable unless there’s a very good reason to make them mutable….If a class cannot be made immutable, limit its mutability as much as possible.
Effective Java
Use as Keys
Immutable objects also make good candidates for use as entity identifiers, or as keys in either Maps or Sets; keys in dictionary collections in Java or C# cannot change value while being used as a key, so immutable objects make great keys.
Inherently Thread-Safe
Immutable objects are also automatically thread-safe and have no synchronization issues (see pp. 46-54 of Java Concurrency in Practice). They can also never exist in unknown or undesirable state because of an exception.
Formally, a class is immutable if and only if:
-
it is final, preventing problems with polymorphism;
-
none of its methods change its own public state; and
-
any mutable fields it has are completely inaccessible to external classes.
How to Make a Java or C# Object Immutable
If you are working in Java or C#, you need to do the following to make an object immutable (adapted from Neal Ford’s article Functional thinking: Immutability):
-
Make all fields final. When you define fields as final in Java, you must either initialize them at declaration time or in the constructor.
-
Make the class final so that it cannot be overridden. If the class can be overridden, its methods’ behaviors can be overridden as well, so your safest bet is to disallow subclassing. Notice that this is the strategy used by Java’s String class.
-
Provide at least one constructor, and set whatever state it will contain in the constructor. If you have no state to set, why do you have an object? Static methods on a stateless class would work just as well. Thus, you should never have a no-argument constructor for an immutable class .
-
Do not provide any mutating methods other than the constructor (i.e. all functions must be side-effect-free).
-
Do not provide a no-argument constructor.
-
Implement hashcode and equals (use your IDE to generate the code). Remember to do this for any enclosed value objects too.
-
Not only must you avoid typical JavaBeans-inspired setXXX methods, but you must also be careful not to return mutable object references. The fact that the object reference is final doesn’t mean that you can’t change what it points to. Thus, you need to make sure you defensively copy any object references you return from getXXX methods. Be especially wary of collections!