Aug
21
2009
An exploration of Immutable Types in Java
In Java, many frustrating Exceptions, like a NullPointerException can be avoided with a judicious application of Immutable types. The most common mistake I see when developers are creating an immutable class is that it may *appear* to immutable, but it really isn’t (for several different reasons — some are easy to spot and some are not).
How about an example. Let’s say a developer was tasked to create an Immutable base class, say Automobile. They may produce something like this:
package com.example.project.businessunit;
import java.lang;
public class Automobile {
private String make;
private String model;
public Automobile(String make, String model) {
this.make = make;
this.model = model;
}
public String getMake() {
return this.make;
}
public String getModel() {
return this.model;
}
}
At first glance, it looks good (no setters) but I see two problems with this approach. First, the private keyword only applies to external classes; another developer working on the same business unit could potentially write a setter that assigns a null value to the property. Second, using reflection, the access protection on the class could be removed and then the values could be changed.
The solution in this particular case is to declare the members of the class as final:
private final String make; private final String model;
Then, throwing an exception if a null is assigned via the constructor:
public Automobile(final String make, final String model) {
if(null == make) {
throw new NullPointerException("Automobile make cannot be null");
}
if(null == model) {
throw new NullPointerException("Automobile model cannot be null");
}
this.make = make;
this.model = model;
}
Here is another common problem I’ve encountered with incorrect implementations of Immutable Types. Let’s start off with some code:
package com.shepherdinteractive.example.immutable;
import com.shepherdinteractive.example.immutable.components.Engine;
public class Automobile {
private final Engine engine;
public Automobile(final Engine engine) {
this.engine = engine;
}
public Engine getEngine() {
return engine;
}
}
At first glance, the code looks OK for an immutable class, but there are actually two problems. The first problem occurs in the constructor. Variables that contain constructed objects in Java are actually reference variables. Therefore, this.engine points to the same object the caller passed in. In other words, when creating an instance of the Automobile class with Engine parameterized constructor, the caller still has a reference to the object held in the internal variable this.engine. The following code will help illustrate the point:
package com.shepherdinteractive.example.immutable.test;
import com.shepherdinteractive.example.immutable.Automobile;
import com.shepherdinteractive.example.immutable.components.Engine;
public class Tester {
public final static void main(final String[] args) {
Engine n54b30 = new Engine(2979);
Automobile genericCar = new Automobile(n54b30);
n54b30.setDisplacement(3000);
// Assume the toString() method outputs displacement value.
System.out.println(genericCar.getEngine());
}
}
Running the program on the command line will produce something like this:
> [java] com.shepherdinteractive.example.immutable.components.Engine[3000]
As you can see, the value actually shouldn’t be 3000, it should be 2979.
The second problem occurs in the getter. When the Engine is returned from the get method, the caller will get a reference to the same instance as the one contained in the supposedly immutable type Automobile. Note that the final keyword doesn’t protect you because it will only block someone from changing the Engine to which the variable engine points to; even when declared as a final, the contents of the Engine object stored in the engine variable can be modified.
The solution to this problem is to make copies of mutable objects when creating new instances of classes. So the constructor would be re-written thus:
public Automobile(final Engine engine) {
this.engine = new Engine(engine.getDisplacement());
}
And here’s the re-written getter:
public Engine getEngine() {
return new Engine(engine.getDisplacement());
}
This notion of copying mutable members of immutable objects can also apply to mutable objects when coding for encapsulation (when you only want to expose the interface, not the implementation details, of a class to the users of that class). This type of defensive copying is costly (ie. this is precisely the reason why they tell you to use StringBuffer for repetitive String manipulations), so please be aware of this. You could easily end up creating applications that eat memory the way I eat sushi (not a good thing).
With the proper implementation of Immutable Types, there is no way that another developer in your team can accidentally (or on purpose) break your encapsulation because they can never get a reference to the internal object. Furthermore, with proper understanding and application, you can create very robust thread-safe software using the fundamental techniques outlined in this article.
Leave a Reply
You must be logged in to post a comment.