Immutability
Posted by calvinkrishy on November 2, 2006
Strings in Java are immutable. That is to say once you create an object of type String no amount of hand-waving, object wrangling can change that object. I will throw some light on immutability in this posting and in a subsequent post talk about const-ness.
Immutability guarantees that an object once created stays the same until the program terminates. The JLS mentions that String objects have a constant unchanging value stated otherwise Strings are immutable.
Immutability is achieved by following two simple rules
1. The class is declared final thereby ensuring that no other class can sub-class.
2. The properties of the class are declared private and the only way to set these properties is through the constructor. If there are methods in the class that need to operate on these properties and return a object then these methods should return new objects and not modify the property in anyway.
A quick note on the ‘final’ keyword before we start off. When the keyword final is attached to a variable it
is guaranteed that a subsequent assignment to that variable would not be allowed. But this does not mean the object thus created cannot be changed. The state of the object can still be changed by using methods exposed by the class for example setter methods. Therefore, just marking a variable final does not in any way guarantee immutability as long as the class that object belongs to has methods that allow the modification of the property.
The important point to remember when programming immutable classes in Java is that immutability is built into the class and is not a property of the object. What this means in simple terms is that the quality of an object to be changed depends on the way the class is designed and not on the context in which the object is used. Contrast this with const functionality provided by C++ – a language that Java derives much syntatic sugar from. Declaring a variable const in C++ guarantees that that particular object would not be modified. Calling a setXXX type method on a const object would result in compilation error. You could create a different object of the same class and that can be made modifiable.
This const-type behavior is achieved in Java using interfaces. More on that in a subsequent post.
Now onto immutability in a POJO. Consider the sample code below:
public class Parent{
private int data;
Parent(int data){
this.data = data;
}
void setData(int data){
this.data = data;
}
int getData(){
return this.data;
}
public static void main(String args[]){
final Parent c = new Parent(5);
System.out.println(c.getData());
/*The following is not allowed since c is declared final
c = new Parent(6);
But the class can still be changed using the setter provided!*/
c.setData(4);
}
}
Since our aim here is to make Parent immutable we remove the setter method thereby making it mandatory for the creators of the Parent objects to initialize data using the constructor. The class does not have any setter methods ensuring that there is only one way to set the value of the property. We remove the final keyword also from the variable since all we are interested here is to make the objects created immutable and not the variable that points to them.
public class Parent{
private int data;
Parent(int data){
this.data = data;
}
int getData(){
return this.data;
}
public static void main(String args[]){
Parent c = new Parent(5);
System.out.println(c.getData());
/* The following cannot be done since the method just doesn't exist!
c.setData(4); */
}
}
Mm., so it appears we have achieved what we set out to achieve without making the class final. The property is private and there are no methods that allow the user to change the property. Or did we really achieve immutability? Since the class is non-final a sub-class could extend it and provide a mutable version. How does this affect immutability? Let’s take a look.
class Parent{
private int data;
Parent(int data){
this.data = data;
}
final int getData(){
return this.data;
}
public int hashCode(){
return 37 * 17 + this.data;
}
}
class Child extends Parent{
private int data;
Child(int i){
super(i);
this.data = i;
}
void setData(int data){
this.data = data;
}
public int hashCode(){
return 37 * 17 + this.data;
}
}
class Thingamagic{
public static void main(String args[]){
Parent c = new Child(6);
((Child)c).setData(7);
}
}
Isn’t this mutable? Since even now calling c.getData() will give us only 6 and not 7. The point is the state of the object has changed since it’s creation and that is exactly what we are trying to avoid. If immutability is guranteed a call to hashcode() on the object should return the same value throughout the life of the object but once this object becomes mutable, the hashcode changes and this can have implications in sections of code that depend on immutability (hashtable keys for example).
The reasons for maintaining immutability are subtle and often escape the vision of a developer. One case that is often cited is the usage of immutable objects as keys to hashtables. Consider the code below:
class Parent{
private int data;
Parent(int data){
this.data = data;
}
int getData(){
return this.data;
}
public int hashCode(){
return 37 * 17 + this.data;
}
public boolean equals(Object obj){
Parent comp = null;
if(obj instanceof Parent)
comp = (Parent)obj;
if(comp.getData() == this.getData())
return true;
else
return false;
}
}
class Child extends Parent{
private int data;
Child(int data){
super(data);
this.data = data;
}
void setData(int data){
this.data = data;
}
int getData(){
return this.data;
}
}
public class FinalVerify{
public static void main(String rags[]){
Hashtable<Parent, String> h = new Hashtable<Parent, String>();
Parent fc = new Child(1);
h.put(fc, "one");
for(Parent f : h.keySet())
System.out.println(f.hashCode());
((Child)fc).setData(2);
for(Parent f : h.keySet())
System.out.println(f.hashCode());
System.out.println(h.get(new Parent(1)));
}
}
Hashtables use the hashCode() and the equals() method to determine the equality of keys and running the code above you’d observe that making the class non-final can result in the key being ‘lost’.
Marking the class final would avoid this problem since no other class would be able to derive from it and create a mutable version.
So a proper Immutable class in our case would be:
final class Parent{
private int data;
Parent(int data){
this.data = data;
}
int getData(){
return this.data;
}
public int hashCode(){
return 37 * 17 + this.data;
}
//Hopefully other useful methods...
}
The String class satisfies both the points since the class is final and it does not expose any methods that allows one to change it’s state once a String object is created. James Gosling in an interview with Bill Venners has provided more insight on this.
References:
Joshua Bloch talks about designing an effective hashCode and equals method among other things in ‘Effective Java’ – http://java.sun.com/docs/books/effective/
Mikael Grev said
If you are interested in immutability there is an article I wrote a while ago: http://www.javalobby.org/members-only/articles/immutable/index.jsp?source=archives
Cheers,
Mikael
Ricky Clarkson said
Your code samples seem aimed at 1.5 without generics, which seems odd – untyped collections but foreach loops. I just looked back through the code, and I’d guess that your blogging software swallowed your generics.
I would also mention anonymous classes as a way of implementing immutability. If a class has no fields then instances of it are immutable. Of course, anonymous classes generate fields from local variables of the enclosing scope, but they are final.
interface Complex
{
double getReal();
double getImag();
}
public static Complex complex(final double real,final double imag)
{
return new Complex()
{
public double getReal()
{
return real;
}
public double getImag()
{
return imag;
}
};
}
Then you get immutability for no extra effort – you can no longer make the mistake of forgetting ‘final’ on the class definition, as it is implicitly final already.
Not only that, but the perceived effect of implementing an interface anonymously is that nobody thinks “I’d like to subclass that, why can’t I?”.
calvinkrishy said
@Mikael
Thanks, that was an informative article.
@Ricky
Nice factory patternish approach but one downside I guess is that instead of having to maintain one single class, we now have to take care of an interface and an anonymous class.
Yes, wordpress seems to have swallowed the generics type definition
. Thanks for pointing that out! Fixed that now.
const-ness in Java « Just about anything said
[...] Comments (RSS) « Immutability [...]