Sunday, April 9, 2017

One of the common misconceptions among many Java Programmer is that a class with all final fields automatically becomes Immutable. This is not correct, you can easily break immutability of certain class if the final field it contains is a mutable one, as we'll see in this article. One of the most common examples of this is a java.util.Date fields e.g. birthDay, expirtyDate, or joiningDate sort of fields. You have to be extra cautious to keep your class' immutability intact with mutable fields. The most common mistake in this regard happens when Java programmer return reference of the original object when a client ask e.g. getBirthDay() returns the date object pointed by birthDay field. When you return a reference to a mutable object, you are sharing ownership of that reference with whoever receives it. This can break invariant, such as immutability.
Another example of this kind of pattern which can break immutability is returning collection or array from the getters method e.g. getListOfBooks() returns a list of books. Once clients get that list it can add new books, remove books and even modify employees without consulting you, hence bypassing any rules you have setup to add or remove books from that list.

So, even though, the field which is pointing to Date or Collection or array object is final, you can still break the immutability of the class by breaking Encapsulation by returning a reference to the original mutable object.

How to preserve Immutability?

There are two ways to avoid this problem, first, don't provide getters to mutable objects if you can avoid it. If you must, then consider returning a copy or clone of the mutable object. If you are returning a collection, you could wrap it as an unmodifiable collection. Since we cannot make an array final or unmodifiable in Java, I suggest avoiding returning an array, instead return an ArrayList by converting an array to ArrayList as shown here.

Now, let's see some code to understand this mistake in little bit more detail and then we'll see the right code to solve this problem by preserving both Encapsulation and Immutability of class.

I have a simple POJO called Person which has 3 fields, name which is String, birthday which is Date, and hobbies which are in a list of String. All these fields are final, but I'll show you how you can still break the immutability of Person class by returning a reference to the mutable object to the client.

Remember, an Immutable object cannot be changed once created, hence it's value will always be same and any modification on an Immutable object should return a new Immutable object e.g. String is Immutable and when you call toUpperCase() or toLowerCase() or trim() you get a new String object.

Here is our Java Program to demonstrate that final fields are not enough to make a class Immutable in Java:

You can see that we have managed to change the state of Person object by changing its birthdate and hobbies. This means the Person class is not Immutable even its all fields are final. Now, let's see how we can solve this problem. If you remember, String is Immutable in Java and you cannot change its content after creating it.

You can see that in the getBirthDay() method, I am returning a clone of Date object and in the getHobbies() method, I am returning and unmodifiable collection, which means if a user tries to add another hobby it will fail.

In Java, Strings are immutable but most objects are not. Whenever you have a mutator or setter method e.g. setXXX or addXXX that returns void chances are good that the object is mutable. Some of the common examples are ArrayList and LinkedList.

To make you objects immutable make sure they only have final fields that are not arrays (arrays are always mutable in java) and only return clone or copy of the mutable object from getter method if you have to, best is to avoid returning references of mutable object altogether.

That's all in this article if you are interested in learning core Java concepts like this, I suggest you read Effective Java, one of the best book for every experienced Java programmers.