Saturday, March 19, 2011

How to make a Java class Immutable

Dear reader,
Today's discussion is about: How to make a Java class Immutable. Immutable means non-changeable.

Immutable means once an object is CREATED, you can't make any changes in that. 
Change means: Changing values of instance variables of that Class (check code for more details).

Ex: Take class java.lang.Thread. It is a normal (Mutable) class and having instance variables like name, priority etc. 
See the below example:
======================
public class ThreadClass {
    public static void main(String[] args) {
        Thread t1=new Thread();
        System.out.println("Thread before change:"+t1);
        System.out.println("Hashcode before change: "+t1.hashCode());

        t1.setName("DeepakThread");   //Changing name
        t1.setPriority(6);            //Changing priority
        
        System.out.println("Thread after change:"+t1);
        System.out.println("Hashcode after change: "+t1.hashCode());
    }
}
//Output:
Thread before change:Thread[Thread-0,5,main]
Hashcode before change: 1854077
Thread after change:Thread[DeepakThread,6,main]
Hashcode after change: 1854077

======================
If you see the above example output, you notice that HashCode is SAME and setter method worked fine. And so values of 
properties "name, priority" of thread object are altered. 

Now this is not possible in IMMUTABLE class.

So if you try to do a change an Immutable Class's object, NEW object will be created every-time you attempt to do so, in short NEW HASHCODE value.

======================Check below Example====================
String is an Immutable class, so once assigned a value, the contents can't be modified in the same object, 

//Immutable.java
public class Immutable {
    public static void main(String[] args) {        
        String x=new String("Deepak");      //new object created
        //x.set###();                       //No setter method is given, so nothing can be changed.
        System.out.println("X: hashCode: "+x.hashCode());

        x.replace("D", "C");      //No impact on "x" here but in above Thread example it was impacted.
        System.out.println("X: hashCode after replacement: "+x.hashCode()+", Value: "+x);
        
        x=x.replace("D", "C");  //Change impacted BUT NEW Object is created internally.
        System.out.println("X: hashCode after actual replacement: "+x.hashCode()+", Value: "+x);
    }
}

//Output:
X: hashCode: 2043177526
X: hashCode after replacement: 2043177526, Value: Deepak
X: hashCode after actual replacement: 2014548375, Value: Ceepak

======================

You must be knowing that String class is a final class. Modifier "final" is used to indicate that the 
class can not be extended. This means an IMMUTABLE class must be final.

Also all instance variables must be declared final so that they can be initialized at ONLY once.
Only once initialization should happen only inside the Constructor as there should be NO Setter() method in an IMMUTABLE class. 

But If you are forced to create a setter method then inside setter(), return a new object with new modified values 
as "replace()" method does in String class. But this is a wrong practice.
Remember you must write getter() methods, else how values are accessible outside ????

Also see: String and StringBuffer both are final classes but only first one is Immutable, later is Mutable. Hence just making a class
final will not make your class as an IMMUTABLE.

So the final points are:
A class will be immutable only if all of the following are true:

1) All of its fields are final.
2) The class is declared final.
3) The constructor should set the variable values only once. Once set, shouldn't be modified.
4) Any fields that contain references to Mutable classes like "Date" and Mutable objects such as Arrays, Collections:
    -Should be private
    -Should never return or otherwise exposed to callers (means no setter() methods)
    -Do not change the state of the referenced objects after construction.


Since Immutable Classes are created once once, it can be shared freely among many Objects ex: License Keys (are encrypted strings)
can be shared in entire application. Best use in Fixed Keys Maps, Sets etc.
-----------------------------END---------------------------------------

No comments:

Post a Comment