Objects whose state can’t be changed after its creation are called immutable objects. It is considered an effective strategy to make objects immutable if they are going to be used in multithreaded applications. Java’s built-in Strings, Integers are all immutable objects and so there must be some solid reasoning behind that. In this article, lets try to explore that.
Questions:
- Why would we create an object whose state can’t be changed?
- When and how to use immutable objects?
- How to make java object immutable?
The primary use of making objects immutable is for thread safety and concurrency. By making an object immutable, we are guaranteed that our object’s state wouldn’t be altered from its initial/invariant state. This allows multiple threads to share immutable threads without fear of any thread modifying its original state.
It is important to understand that immutable is not a class/object/keyword in Java and so our objects aren’t immutable unless we force it with some techniques. So it is the responsibility of developers to enforce immutability in objects when needed. Lets start with a mutable object and see how to make an object immutable.
import java.util.Date; class EmployeeMut { private String name; private Date salaryDate; private Double salary; public EmployeeMut(String name, Date salaryDate, Double salary) { this.name = name; this.salary = salary; this.salaryDate = salaryDate; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Date getDate() { return salaryDate; } public void setDate(Date date) { this.salaryDate = date; } public void setSalary(Double salary) { this.salary = salary; } public Double getSalary() { return salary; } } public class Employee { public static void main(String args[]) { EmployeeMut emp = new EmployeeMut("Emp1", new Date(), 1000.00); emp.setName("Emp2"); emp.setSalary(2000.00); emp.setDate(new Date()); } }
This is an example of a mutable object. Lets look at the rules to be followed in order to convert this into an immutable object.
How To Implement An Object Immutable?
- 1. Avoid exposing any setter methods/any other methods that might allow clients to modify the object’s instance variables values. In our example class, it can be seen that the clients are able to successfully alter the values after the object creation as shown by emp.setName(“Emp2”) though initially it was initialized to “Emp1” which means value has changed from its initial state of Emp1. So avoid exposing any such methods to the clients.
- 2. Make all fields private and final.
import java.util.Date; class EmployeeMut { private final String name; private final Date salaryDate; private final Double salary; public EmployeeMut(String name, Date salaryDate, Double salary) { this.name = name; this.salary = salary; this.salaryDate = salaryDate; } public String getName() { return name; } public Date getDate() { return salaryDate; } public Double getSalary() { return salary; } } public class Employee { public static void main(String args[]) { EmployeeMut emp = new EmployeeMut("Emp1", new Date(), 1000.00); } }
From my first example, I have just removed all references to the setter/modify methods and also made all fields private and final.
- 3. Our class “EmployeeMut” is public and so any client can extend it. When a child class extends it, there are chances of the child class modifying some of the parent class behavior/fields. To avoid this, make the class as final.
final class EmployeeMut { private final String name; private final Date salaryDate; private final Double salary;
What if we didn’t want the class to be final. There is an alternate technique than using “final” class and that is to use private constructors and static factory methods to call those private constructors. The class will be public but since constructors are private, no one will get direct.
class EmployeeMut { private String name; private Date salaryDate; private Double salary; private EmployeeMut(String name, Date salaryDate, Double salary) { this.name = name; this.salary = salary; this.salaryDate = new Date (salaryDate.getTime()); } public static EmployeeMut valueOf(String name, Date salaryDate, Double salary) { return new EmployeeMut(name, salaryDate, salary); } }
Comparing it with previous code, I have just removed the final keyword and added a private constructor with a static method calling that constructor. As a matter of fact, Java developers are advised by experts to use the static factories invocation instead of constructors. So from now on, lets try to use static factories to create new objects from client programs rather than use constructors. Here is how the client creates a new object.
Employee mtu = EmployeeMut.valueOf("name", new Date(), 1000.00);
Have we done all the things to make the class immutable? Is there something lurking which the client can modify? Yes. Consider the following client program.
public class Employee { public static void main(String args[]) { Date date = new Date(); EmployeeMut emp = new EmployeeMut("Emp1", date, 1000.00); System.out.println("Month : " + emp.getDate().getMonth()); date.setMonth(date.getMonth() + 5); System.out.println("Month : " + emp.getDate().getMonth()); } }
As it can be seen, clients are able to modify the “date” reference that was used in emp reference. This violates the rules of immutability. The reason is that if we check Java docs, java.util.Date is a mutable object. So do we need to make the java.util.Date immutable to make our example class immutable? No. There is another way to do it and that is the 4th rule.
- 4. If our class has fields referring to mutable objects, we should make defensive copies of these references before passing it to the client. This way, the reference will always refer to a new object whenever any of the methods containing that mutable object are accessed. This needs to be carefully checked as if we miss it a single place, even then the class is not mutable.
public EmployeeMut(String name, Date salaryDate, Double salary) { this.name = name; this.salary = salary; this.salaryDate = new Date(salaryDate.getTime()); public Date getDate() { return new Date(this.salaryDate.getTime()); }
As a workaround to follow the 4th rule, I have just changed the code to always return a new object wherever the Date object will be used. This will ensure that new Date() instances are always returned, thereby initial Date() instance is unmodified. Run the same sample client code after making this change and you will be surprised to see the same output because we have made the Date reference object immutable too. Now our EmployeeMut class is immutable.
Having seen the rules for making objects immutable, lets start looking at some of the advantages that these immutable objects has for Java developers.
Advantages Of Immutable Objects
1. Thread-safety and hence no issues of synchronization.
2. We don’t need to implement a clone or copy constructor. Why? Because the clone or copy will produce the same object as original and so it doesn’t make any sense to use them in immutable objects. I will try to cover copy() and clone() in a separate article.
3. Another technique that is often used is to cache the frequently used instances in immutable objects. Every call to those often used instances don’t take up much time. Eg.,
Taken from BigDecimal open source code
// cache of common small BigDecimal values
new BigDecimal(BigInteger.ZERO, 0, 0); new BigDecimal(BigInteger.ONE, 1, 0); new BigDecimal(BigInteger.TEN, 10, 0);
So the client program would be using these already created instances instead of recreating new objects by themselves.
BigDecimal big = BigDecimal.TEN; System.out.println(big.toString());
4. Also, this immutable property helps by storing the cached results. Suppose, we have a method which should always return a unique value based on some instance variable calculation. But we know that calls to that method with same object should return the same value as our objects are immutable now. Look at the following example where such a technique is used:
private int hash ; // cached result .. by default, hash = 0 public int hashCode() { int h = hash; if (h ==0 && value.length > 0) { /* calculate hash value using some technique ..... ...... */ hash = h; } return h; }
In order to employ this technique, we have to relax the rule that all fields should be made final and that is not a big concern here as the object will still remain immutable. We have a new variable “hash” and made it non-final here to store the cached results on subsequent invocations with same “EmployeeMut” object. Only on the first time, hash value will be calculated as hash value will be zero only at the beginning and from then on, the previously calculated “h” cached variable result will be returned to the caller. This eliminates calculations that needs to be done every time even for same object invocations though we know the value returned should be the same. This is because we know our object is immutable i.e., its value doesn’t change and hence calculations even based on instance variables in our case will return similar values for same object invocation.
Summary
So far, it’s all been good. Then the question comes as to why not every developers use only immutable objects? One of the disadvantages according to java experts about immutable objects is the need for creation of separate new objects for each distinct value (recollect the 4th rule!!!!!). So we are using up some extra memory, which will not be used in the case of mutable objects. But this only concern is not a valid reason for developers to opt for mutable objects. Java experts advise that we should always be using mutable objects and if not possible, try to preserve the immutability of that object as much as possible.
Thats it about Java immutable objects for today. Hope you enjoyed reading. Pass on your comments/questions if any.
This article is originally published at Java tutorials – Lets jump into the ocean, re-posted here with authors permission and as part of the JBC program.