Jun 10, 2015

How to make a class immutable in Java?

Below rules needs to be followed while designing a class to make sure any object created is immutable.
  1. Make the class final.
  2. Make all members private and final.
  3. For a mutable member, getters and setters needs to be given special emphasis.
      • Never assign the actual parameter to the member object. Assign a copy instead.
      • In getter method, do not return actual object but a copy of it.
The reasoning behind making the class final is very subtle and often overlooked. If its not final people can freely extend your class, override public or protected behavior, add mutable properties, then supply their subclass as a substitute. By declaring the class final you can ensure this won't happen.
To see the problem in action consider the example below:
public class ProblemExample {
/**
* @param args
*/
public static void main(String[] args)
{
System.out.println("Hello World!");
OhNoMutable mutable = new OhNoMutable(1, 2);
ImSoImmutable immutable = mutable;
/*
* Ahhhh Prints out 3 just like I always wanted
* and I can rely on this super immutable class
* never changing. So its thread safe and perfect
*/
System.out.println(immutable.add());
/* Some sneak programmer changes a mutable field on the subclass */
mutable.field3=4;
System.out.println(immutable.add());
/* Why is this buggy piece of crap printing 7 and not 3
It couldn't have changed its IMMUTABLE!!!!
*/
}
}
/* This class adheres to all the principles of
* good immutable classes. All the members are private final
* the add() method doesn't modify any state. This class is
* I didn't declare the class final. Let the chaos ensue
* just a thing of beauty. Its only missing one thing
*/
public class ImSoImmutable
{
private final int field1;
private final int field2;
public ImSoImmutable(int field1, int field2){
this.field1 = field1;
this.field2 = field2;
}
public int add(){
return field1+field2;
}
}
/*
This class is the problem. The problem is the
overridden method add(). Because it uses a mutable
member it means that I can't guarantee that all instances
of ImSoImmutable are actually immutable.
*/
public class OhNoMutable extends ImSoImmutable { 
public int field3 = 0;
public OhNoMutable(int field1, int field2) {
super(field1, field2);
}
public int add(){
return super.add()+field3;
}
}
In practice it is very common to encounter the above problem in Dependency Injection environments. You are not explicitly instantiating things and the super class reference you are given may actually be a subclass.

0 comments:

Post a Comment