Object immutability in Java

I’ve seen many developers struggling with subtle mutability issues in Java. This article will expose the major gotchas of object immutability.

I Definition

An object is said to be “immutable” when, after its initialization (object creation), its internal state does not change.

The definition looks quite simple and straightforward but the “its internal state does not change” part is very hard to obtain.

One simple example of immutable object in Java is the String class. One created, the text content of a String instance can never change. Everytime you call a helper method on this String instance (concat(), subString(), toLowerCase()…), a new instance of String is returned, the original String instance remains unchanged.


String myString ="TEST";

String subString = myString.toLowerCase();

System.out.println("myString == subString ? "+ (myString == subString));

The above code will display as output:

myString == subString ? false

 

II Immutability and encapsulation

The first idea to make an object immutable is to declare all its attributes “private” and to remove all setters. But without setters we have to find a way to initialize these private attributes. One solution could be declaring them as constructor arguments. During the object initialization, we provide all the attributes. After the initialization, since the attribute are privates they cannot be accessed from the outside, thus guaranting immutabitily.

Below an example:

public class ImmutableByEncapsulation
{
	private List<String> items;

	public ImmutableByEncapsulation(List<String> items) {
		this.items = items;
	}

	@SuppressWarnings("unused")
	private ImmutableByEncapsulation() {
		super();
	}

	public String getItemByPosition(int position)
	{
		if (position >= 0 && position < this.items.size())
		{
			return this.items.get(position);
		}

		return null;
	}
}

The above code shows an ImmutableByEncapsulation class with a constructor accepting a List of String (default constructor is made private to force initialization with List of String).

Once created, the internal items list cannot be accessed from the outside so cannot be modified.

Indeed there is a big flaw in this example. Let’s check the below code:


List<String> items = new ArrayList<String>();

items.add("table");
items.add("chair");
items.add("desk");

ImmutableByEncapsulation ob = new ImmutableByEncapsulation(items);

...
ob.getItemByPosition(2);
...

items.add("sofa"); // Immutability broken !!!
...

The previous design is completely broken because the main code still have a reference to the items list passed as constructor argument. The internal items list can be modified outside of ImmutableByEncapsulation.

To fix this, we need to make a copy of the items list passed as constructor argument.

public class ImmutableByEncapsulation
{
	private List<String> items;

	public ImmutableByEncapsulation(List<String> items) {
		this.items = new ArrayList<String>(items);
	}
	...
}	

This way, any modification made to the items list after ImmutableByEncapsulation initialization will not impact its internal state.

 

III Immutability and object graph

The above solution makes the ImmutableByEncapsulation immutable because the list of items is of type String, which is immutable. Had we changed it to another non-immutable type, it wouldn’t work.

Let’s turn the list of String into a list of Furniture, a custom type:

public class Furniture
{
	private String type;
	private int quantity;

	public Furniture(String type, int quantity) {
		super();
		this.type = type;
		this.quantity = quantity;
	}

	public String getType() {return type;}

	public void setType(String type) {this.type = type;}

	public int getQuantity() {return quantity;}

	public void setQuantity(int quantity) {	this.quantity = quantity;}	
}

public class ImmutableByEncapsulation
{
	private List<Furniture> items;

	public ImmutableByEncapsulation(List<Furniture> items) {
		this.items = new ArrayList<Furniture>(items);
	}
	...	
}

...
...
List<Furniture> items = new ArrayList<Furniture>();

Furniture table = new Furniture("table",2);
Furniture chair = new Furniture("chair",10);
Furniture sofa = new Furniture("sofa",1);

items.add(table);
items.add(chair);
items.add(sofa);

ImmutableByEncapsulation ob = new ImmutableByEncapsulation(items);

...
ob.getItemByPosition(2);
...

chair.setQuantity(100) // Immutability broken !!
...

This is the immutability cascade issue. To make an object really immutable, all its internal object graph should be immutable.

Please note that it was easy to make our Furniture class immutable. All we need to do is to remove the setter methods. Since the String and primitive types are immutable, the class would be immutable after its initialization.
 

IV Immutability and cloning

For real life use cases it is sometimes required that the immutable object exposes its internal state as read-only value. In this case, two possible solutions:

  • method returned values are immutable object. This implies that the internal state of the object is composed of immutable components as well
  • method returned values are a clones of an internal components. In this case the returned clone can be modified outside of the object without breaking immutability contract

Let’s see an example.

public class ImmutableByCloning
{
	private List<MutableObject> items;

	public ImmutableByCloning(List<MutableObject> items) {
		this.items = new ArrayList<MutableObject>();
		for (MutableObject mutableItem : items)
		{
			this.items.add(mutableItem.clone());
		}
	}

	@SuppressWarnings("unused")
	private ImmutableByCloning() {}

	public List<MutableObject> getItems()
	{

		List<MutableObject> returnedItems = new ArrayList<MutableObject>();
		for (MutableObject mutableItem : this.items)
		{
			returnedItems.add(mutableItem.clone());
		}

		return returnedItems;
	}
}

In the above code, we can see that there is a double level of cloning.

First in the constructor, a new list is created in which we put all the clones of MutableObject. The list is cloned (new ArrayList) and each element inside the list is also cloned.

This pattern ensures that no reference of the internal state (reference on items attribute or on each MutableObject in the items list) leaks outside of the class.

The same pattern is applied to the getter getItems() for immutability.
 

V Protect your immutables !

Even if you have spent hours designing a perfectly immutable class (by implementing clone() for all the internal components). There is still one possibility to ruin your hard work.

How ?

Simply by extending your class and re-defining the safe getters and removing object cloning pattern!

To really enforce strict immutability, you need to declare your class as final so it cannot be extended.

 

VI Conclusion

After these few examples, we can see that any time a reference to an internal component is accessible from outside of the object (constructor argument, getters or simple method returned values) there is a risk of modification and the immutability can be broken, unless the return values are themselves immutable.

From this observation, some rules of thumb can be drawn. To make an object immutable

  • either all of its internal components shoud be immutable
  • or cloning should be applied to all constructor arguments and method returned values. This implies that all the internal components redefine the clone() method properly

Neither of the above conditions is simple. Making a class truly immutable is hard, making the internal component graph immutable is even harder. The cloning solution is not a piece of cake either. Redefining the clone() method in a proper manner to implement deep copy is a dawnting task, especially when you have a complex object graph.

There are some articles over there on the Web with object serialization as a solution for object deep copy but the impact on performance is huge. Object serialization can be sometimes hundred times slower than manual clone() redefinition.

At the bottom line, object immutability is a quite complex subject. Immutability in a “classical” Java application is usually not really an issue.

Immutability becomes useful and interesting when dealing with multi-threaded applications because immutable objects are by nature thread-safe and save from synchronization headache.

 
 
 

5 Comments

  1. Pingback: Final variable in Java « DuyHai's Java Blog

  2. Joe

    Hi DuyHai,

    Thanks for this article. However I have one question. To make an Immutable List out of the Furniture object should we implement the Cloneable interface in the Furniture object?

    Regards,

    Joe

    Reply
    1. DuyHai DOAN

      Hello Joe

      The Clonable interface in Java forces you to override the default clone() method of the Object class. Marking a class with Clonable interface without overriding clone() is pretty useless. Without overriding the clone() method, you’ll use the default implementation which will give you a shallow copy. That’s certainly not what you want.

      Bottom line, you must implement yourself the deep copy of your instance (and deep copy of the object graph) in the clone() method. It’s a hard task.

      Reply
  3. Pingback: Cache abstraction in Spring 3 « DuyHai's Java Blog

  4. Vidya

    Excellent Article…!!!!
    So many things 2 takecare 2 make an object immutable….!!!!!
    Many new concepts learned after reading ur post…..Tnxx a lot Duy….!!!!!

    Reply

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.