Immutable Objects in Java
An overview plus some practical tips on immutable objects in Java.
Join the DZone community and get the full member experience.
Join For FreeAn immutable object is an object that will not change its internal state after creation.
Immutable objects are very useful in multithreaded applications because they can be shared between threads without synchronization.
Immutable objects are always thread safe.
Threads Are Everywhere
It is not important if you write explicit multithreaded application or not, often you work in a multithreaded environment without directly managing thread instances.
Here are few example of multithreaded applications where the programmer doesn't manually invoke the creation of new threads:
Web applications because servlets are handled by a pool of threads
Swing desktop applications where threads handle GUI events
Timer instances create new threads to handle future task executions
Creating an Immutable Object
To create an immutable object you need to follow some simple rules:
Don't add any setter method
Declare all fields final and private
If a field is a mutable object create defensive copies of it for getter methods
If a mutable object passed to the constructor must be assigned to a field create a defensive copy of it
Don't allow subclasses to override methods.
Now we discover point by point the reasons of the five rules explained before.
Don't add any setter method
If you are building an immutable object its internal state will never change. Task of a setter method is to change the internal value of a field, so you can't add it.
Declare all fields final and private
A private field is not visible from outside the class so no manual changes can't be applied to it.
Declaring a field final will guarantee that if it references a primitive value the value will never change, if it reference an object the reference can't be changed. This is not enough to ensure that an object with only private final fields is not mutable. Here is an example showing an object with a private final field and an example on how to mutate its internal state:
public class DateContainer {
private final Date date;
public DateContainer() {
this.date = new Date();
}
public Date getDate() {
return date;
}
}
....
DateContainer dateContainer = new DateContainer();
System.out.println(dateContainer.getDate());
dateContainer.getDate().setTime(dateContainer.getDate().getTime() + 1000);
System.out.println(dateContainer.getDate());
// Now dateContainer date is 1 second after
If a field is a mutable object create defensive copies of it for getter methods
We have seen before that defining a field final and private is not enough because it is possible to change its internal state. To solve this problem we need to create a defensive copy of that field and return that field every time it is requested.
Here is the previous class with that modification:
public class DateContainer {
private final Date date;
public DateContainer() {
this.date = new Date();
}
public Date getDate() {
return new Date(date.getTime());
}
}
....
DateContainer dateContainer = new DateContainer();
System.out.println(dateContainer.getDate());
dateContainer.getDate().setTime(dateContainer.getDate().getTime() + 1000);
System.out.println(dateContainer.getDate());
// Now dateContainer date is not changed because we changed the copy,
// not the original date
If a mutable object passed to the constructor must be assigned to a field create a defensive copy of it
The same problem happens if you hold a reference passed to the constructor because it is possible to change it.
Here we show a modified example of DateContainer that accept a Date for the constructor and we will see how it is possible to change its internal state:
public class DateContainer {
private final Date date;
public DateContainer(Date date) {
this.date = date;
}
public Date getDate() {
return new Date(date.getTime());
}
}
....
Date date = new Date();
DateContainer dateContainer = new DateContainer(date);
System.out.println(dateContainer.getDate());
date.setTime(date.getTime() + 1000);
System.out.println(dateContainer.getDate());
// Now dateContainer date is 1 second after also if the getter method
// create a defensive copy of date. We changed the reference passed to the
// constructor, not the copy.
So holding a reference to an object passed to the constructor can create mutable objects. To solve this problem it is necessary to create a defensive copy of the parameter if they are mutable objects:
public class DateContainer {
private final Date date;
public DateContainer(Date date) {
this.date = new Date(date.getTime());
}
public Date getDate() {
return new Date(date.getTime());
}
}
....
Date date = new Date();
DateContainer dateContainer = new DateContainer(date);
System.out.println(dateContainer.getDate());
date.setTime(date.getTime() + 1000);
System.out.println(dateContainer.getDate());
// Now dateContainer date is not changed. We create a copy on the constructor
// so a change to the external date will not affect the internal state of
// DateContainer instance
Note that if a field is a reference to an immutable object is not necessary to create defensive copies of it in the constructor and in the getter methods it is enough to define the field as final and private. As an example of common immutable objects there are String, all primitive wrappers (Integer, Long, Double, Byte....), BigDecimal, BigInteger.
Don't allow subclasses to override methods
If a subclass override a method it can return the original value of a mutable field instead of a defensive copy of it.
To solve this problem it is possible to do one of the following:
Declare the immutable class as final so it can't be extended
Declare all methods of the immutable class final so they can't be overriden
Create a private constructor and a factory to create instances of the immutable class because a class with private constructors can't be extended
If you follow those simple rules you can freely share your immutable objects between threads because they are thread safe!
Opinions expressed by DZone contributors are their own.
Comments