17 September, 2014

How to create an Immutable Class in Java?


Immutable means: something which cannot be modified once created. In Java, an immutable class is one, whose objects retain their initial state through out their life-cycle. Objects whose properties/ values cannot be changed after they have been created are immutable objects e.g. java.lang.String

To achieve a design of such a class we need to understand what are the ways in which an object can be modified.

  1. All state variables must be private and final
  2. Remove setters.
  3. Prevent leaking of reference variables by using clone.
  4. Make the class final
     

Explaination:


Making state variables private and final:
State variables must be final, so as to prevent change in their value i.e to make them constants.

state variables can be of either primitive or object types. Not making primitive type state variables as private is fine, but in case the object type state variable is exposed directly, the setter methods on that object can be called to modify the state. Hence, immutability is not achieved.

e.g.

class MakingOfImmutable{
    public final int primitive=0;
    public final MutableObject mutableObj= new MutableObject();
}

class BreakingOfImmutable{
    public static void main(String[] args){
// this should be immutable
       MakingOfImmutable im=new MakingOfImmutable();
 //but we can modify the object in following way:
       im.mutableObj.setMutableValue(new MutableValue());

    }
}

In the above example, the object itself is not being changed, but the state variable is being modified by calling a setter with changed value.


Although, this doesn't apply on primitives,but it is always a best practice not to expose state variables directly, but through getters.

so, the following class is more closer to immutability than the above example

class MakingOfImmutable{
    private final int primitive;
    private final MutableObject mutableObj;
    public MakingOfImmutable(){
    }
    public MakingOfImmutable(int pPrimitive, MutableObject pMutableObject){
         this.primitive=pPrimitive;
         this.mutableObj=pMutableObject;
    }

    public int getPrimitive(){
        return  this.primitive;
    }
    public void setPrimitive(int pPrimitive){
        this.primitive=pPrimitive;
    }
    public int getMutableObj(){
        return  this.mutableObj;
    }
    public void setMutableObj(int pMutableObj){
        this.primitive=pMutableObj;
    }
}

Removing the setters

By removing the setters, and only providing getters, we remove the possibility of change of value of state variables. Also, since setters are removed, we need a way of initializing the state of our object. We use parametrized constructor for that. And in this case, default constructor makes little sense. Hence the following class is closer to immutability than previous example

class MakingOfImmutable{
    private final int primitive;
    private final MutableObject mutableObj;

    public MakingOfImmutable(int pPrimitive, MutableObject pMutableObject){
         this.primitive=pPrimitive;
         this.mutableObj=pMutableObject;
    }

    public int getPrimitive(){
        return  this.primitive;
    }
 

    public int getMutableObj(){
        return  this.mutableObj;
    }
   
}

Prevent Reference Leakage
Java is pass by value, but in case of objects the value is their reference. Hence it behaves like pass by reference.

Consider the following class creating the object of out yet-state MakingOfImmutable class:

class BreakingOfImmutable{
    public static void main(String[] args){
 // create value of state
MutableObject obj = new  MutableObject();
// pass the mutable object in constructor
       MakingOfImmutable im=new MakingOfImmutable(0,obj);
 //but we can modify the object in following way since reference is shared:
       obj.setMutableValue(new MutableValue());

    }
}

Since reference is shared in the constructor, the changes done in obj would be reflected in our mutable class object. Hence, we must prevent leaking of references, a way of doing is by using clone() method.

Same is the case with getters returning object references. The returned object must also be a clone of original object reference.

Hence, following class is closer to immutability

class MakingOfImmutable{
    private final int primitive;
    private final MutableObject mutableObj;

    public MakingOfImmutable(int pPrimitive, MutableObject pMutableObject){
         this.primitive=pPrimitive;
         this.mutableObj=pMutableObject.clone();
    }

    public int getPrimitive(){
        return  this.primitive;
    }
 

    public int getMutableObj(){
        return  this.mutableObj.clone();
    }
   
}


Making the class final
Child classes in Java follows IS-A hierarchy. Any child class can be used as a replacement of its parent. And child class can override parent's implementation or add more methods (in this case setters). Allowing children of our class can lead to creation of mutable versions of our class. Hence, we must make our class as final.

Thus, we arrive at a class which is completely immutable:

final class MakingOfImmutable{
    private final int primitive;
    private final MutableObject mutableObj;

    public MakingOfImmutable(int pPrimitive, MutableObject pMutableObject){
         this.primitive=pPrimitive;
         this.mutableObj=pMutableObject.clone();
    }

    public int getPrimitive(){
        return  this.primitive;
    }
 

    public int getMutableObj(){
        return  this.mutableObj.clone();
    }