Skip to main content

How to create an immutable class in java



An object is immutable if its state cannot change after construction, immutable objects don’t expose any way for other objects to modify their state, the object’s fields are initialized only once inside the constructor and never change again.
In this article, we define the typical steps for creating an immutable class in java, we also shed the light on the common mistakes which are normally done by developers while creating immutable classes.
1. Usage of Immutable classes
Nowadays, the “must-have” specification of every software application is to be distributed and multi-threaded, multi-threaded applications always cause headache to developers since developers are required to protect the state of their objects from concurrent modifications of several threads at the same time, for this purpose, developers normally use the Synchronized blocks whenever they modify the state of an object.
With immutable classes, states are never modified, every modification of a state results in a new instance, hence each thread would use a different instance and developers wouldn’t worry about concurrent modifications.
2. Some popular immutable classes
String is the most popular immutable class in java,  once initialized its value cannot be modified, operations like trim(), substring(), replace() always return a new instance and don’t affect the current instance, that’s why we usually call trim() as the following:
1
2
String alex = "Alex";
alex = alex.trim();
Another example from JDK is the wrapper classes like: Integer, Float, Boolean … these classes don’t modify their state , however they create a new instance each time you try to modify them.
1
2
Integer a =3;
a += 3;
After calling a += 3, a new instance is created holding value: 6 and the first instance is lost.
3. How do we create an immutable class
In order to create an immutable class, you should follow the below steps:
1.     Make your class final, so that no other classes can extend it.
2.     Make all your fields final, so that they’re initialized only once inside the constructor and never modified afterwards.
3.     Don’t expose setter methods.
4.     When exposing methods which modify the state of the class, you must always return a new instance of the class.
5.     If the class holds a mutable object:
§  Inside the constructor, make sure to use a clone copy of the passed argument and never set your mutable field to the real instance passed through constructor,  this is to prevent the clients who pass the object from modifying it afterwards.
§  Make sure to always return a clone copy of the field and never return the real object instance

3.1. Simple immutable class
Let’s follow the above steps and create our own immutable class (ImmutableStudent.java).
ImmutableStudent.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.programmer.gate.beans;

public final class ImmutableStudent {

    private final int id;
    private final String name;

    public ImmutableStudent(int id, String name) {
        this.name = name;
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }
}
The above class is a very simple immutable class which doesn’t hold any mutable object and never expose its fields in any way, these type of classes are normally used for caching purposes.
3.2.  Passing mutable objects to immutable class
Now let’s complicate our example a bit, we create a mutable class called Age and add it as a field to ImmutableStudent:
Age.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.programmer.gate.beans;

public class Age {

    private int day;
    private int month;
    private int year;

    public int getDay() {
        return day;
    }

    public void setDay(int day) {
        this.day = day;
    }

    public int getMonth() {
        return month;
    }

    public void setMonth(int month) {
        this.month = month;
    }

    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

}
ImmutableStudent.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.programmer.gate.beans;

public final class ImmutableStudent {

    private final int id;
    private final String name;
    private final Age age;

    public ImmutableStudent(int id, String name, Age age) {
        this.name = name;
        this.id = id;
        this.age = age;
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public Age getAge() {
        return age;
    }
}
So, we added a new mutable field of type Age to our immutable class and assign it as normal inside the constructor.
Let’s create a simple test class and verify that ImmutableStudent is no more immutable:
1
2
3
4
5
6
7
8
9
10
11
12
public static void main(String[] args) {
        
        Age age = new Age();
        age.setDay(1);
        age.setMonth(1);
        age.setYear(1992);
        ImmutableStudent student = new ImmutableStudent(1, "Alex", age);
        
        System.out.println("Alex age year before modification = " + student.getAge().getYear());
        age.setYear(1993);
        System.out.println("Alex age year after modification = " + student.getAge().getYear());
    }
After running the above test, we get the following output:
1
2
Alex age year before modification = 1992
Alex age year after modification = 1993
We claim that ImmutableStudent is an immutable class whose state is never modified after construction, however in the above example we are able to modify the age of Alex even after constructing Alex object. If we go back to the implementation of ImmutableStudentconstructor, we find that age field is being assigned to the instance of the Age argument, so whenever the referenced Age is modified outside the class , the change is reflected directly on the state of Alex. Check Pass by value OR pass by reference article to deeply understand this concept.
In order to fix this and make our class again immutable, we follow step #5 from the steps that we mention above for creating an immutable class. So we modify the constructor in order to clone the passed argument of Age and use a clone instance of it.
ImmutableStudent.java
1
2
3
4
5
6
7
8
9
public ImmutableStudent(int id, String name, Age age) {
        this.name = name;
        this.id = id;
        Age cloneAge = new Age();
        cloneAge.setDay(age.getDay());
        cloneAge.setMonth(age.getMonth());
        cloneAge.setYear(age.getYear());
        this.age = cloneAge;
    }
Now, if we run our test, we get the following output:
1
2
Alex age year before modification = 1992
Alex age year after modification = 1992
As you see now, the age of Alex is never affected after construction and our class is back immutable.
3.3. Returning mutable objects from immutable class
However, our class still have a leak and is not fully immutable, let’s take the following test scenario:
1
2
3
4
5
6
7
8
9
10
11
12
public static void main(String[] args) {

        Age age = new Age();
        age.setDay(1);
        age.setMonth(1);
        age.setYear(1992);
        ImmutableStudent student = new ImmutableStudent(1, "Alex", age);

        System.out.println("Alex age year before modification = " + student.getAge().getYear());
        student.getAge().setYear(1993);
        System.out.println("Alex age year after modification = " + student.getAge().getYear());
    }
Output:
1
2
Alex age year before modification = 1992
Alex age year after modification = 1993
Again according to step #4, when returning mutable fields from immutable object, you should return a clone instance of them and not the real instance of the field.
So we modify getAge() in order to return a clone of the object’s age:
ImmutableStudent.java
1
2
3
4
5
6
7
8
public Age getAge() {
        Age cloneAge = new Age();
        cloneAge.setDay(this.age.getDay());
        cloneAge.setMonth(this.age.getMonth());
        cloneAge.setYear(this.age.getYear());
        
        return cloneAge;
    }
Now the class becomes fully immutable and provides no way or method for other objects to modify its state.
1
2
Alex age year before modification = 1992
Alex age year after modification = 1992

Benefits of Immutable Classes in Java
As I said earlier Immutable classes offers several benefits, here are few to mention:

1) Immutable objects are by default thread safe, can be shared without synchronization in concurrent environment.
2) Immutable object simplifies development
, because its easier to share between multiple threads without external synchronization.

3) Immutable object boost performance of Java application by reducing synchronization in code.

4) Another important benefit of Immutable objects is reusability, you can cache Immutable object and reuse them, much like String literals and Integers.  You can use static factory methods to provide methods like valueOf(), which can return an existing Immutable object from cache, instead of creating a new one.

Apart from above advantages, immutable object has disadvantage of creating garbage as well. Since immutable object can not be reused and they are just a use and throw. String being a prime example, which can create lot of garbage and can potentially slow down application due to heavy garbage collection, but again that's extreme case and if used properly Immutable object adds lot of value.




Comments

Popular posts from this blog

Yahoo! Calendar "Add Event" Seed URL Parameters

I can't seem to find any official documentation on this, so here are my notes. Some information gathered from  http://richmarr.wordpress.com/tag/calendar/ Other information gathered through trial and error, and close examination of the "Add Event" form on Yahoo!'s site. Yahoo! Calendar URL Parameters Parameter Required Example Value Notes v Required 60 Must be  60 . Possibly a version number? TITLE Required Event title Line feeds will appear in the confirmation screen, but will not be saved. May not contain HTML. ST Required 20090514T180000Z Event start time in UTC. Will be converted to the user's time zone. 20090514T180000 Event start time in user's local time 20090514 Event start time for an all day event. DUR value is ignored if this form is used. DUR 0200 Duration of the event. Format is HHMM, zero-padded. MM may range up to 99, and is converted into hours appropriately. HH values over 24 hours appear to be modulated by 24. Durations t...

Java literals:

Java literals:           A constant value which can be assigned to a variable is known as Literal.If we are assigning any outside range value for any data type ,we will get a compile time error saying Possible Loss of Precision found int required byte. For the integral data types (int ,byte,short,long) : we are allowed to specify a literal value in any   one of the following three forms. ---> Decimal literal (normal way) --->Octa literal (prefixed with 0 ) --->Hexa decimal (prefixed with 0x ) int x=10 ------------> Decimal int x=010 ------------>Octa int x=0X10 -------------->Hexa In the Hexa decimal notation for the extra digits we are allowed to specify either small or upper case a,b,c or A,B,C ( this is one of the few places where java is not case sensitive ). Example: class Sample { public static void main(String add[]) { int i = 10; int j=010; int k=0x10; System.out.println( i+”….”+j...