To create your own immutable class in java, you have to do following steps:
- Declare the class as final so it can’t be extended.
- Make all attributes as private and final so that direct access is not allowed and value can be assigned only once.
- Initialize all the fields via a constructor performing deep copy.
- No setter methods for attributes.
- In getter methods return a copy rather than returning the actual object reference.
- If you have a attribute of Collection type object like like a ArrayList, LinkedList then do the following while returning the data from its getter method:
public List getAddressList(){ return Collections.unmodifiableList(addressList); }
Now lets start writing our own Immutable class.
First we will use the above point #1, #2, #3 and #4 as below:
final class MyImmutable{ private final String name; public MyImmutable(String name) { this.name=name; } public String getName() { return name; } }
The above immutable class holds ok till we have the attributes as primitive type as above.
Now lets add a new attribute of a derived type called Employee:
class Employee { String name; public Employee() { } public Employee(String name) { this.name=name; } }
final class MyImmutable{ private final String name; private final Employee employee; public MyImmutable(String name,Employee employee) { this.name=name; this.employee=employee; } public String getName() { return name; } public Employee getEmployee(){ return this.employee; } }
Somewhere in a client class we will try to create a immutable object and try to modify its value:
public class Manager { public static void main(String[] args) { Employee e1=new Employee("Prasanna"); MyImmutable mi1=new MyImmutable("Object1",e1); System.out.println("Employee name before modification: " + mi1.getEmployee().name); e1.name="Padma"; System.out.println("Employee name after modification: " + mi1.getEmployee().name); } }
The output in console:
Employee name before modification: Prasanna Employee name after modification: Padma
If you see, the immutable object is no more immutable, since we are able to modify its value.
Now you have to use point # 5: Initialize all the fields via a constructor performing deep copy. So basically we have to manually do deep copy of each variable for the derived class.
final class MyImmutable{ private final String name; private final Employee employee; public MyImmutable(String name,Employee employee) { this.name=name; Employee employee2 =n ew Employee(); employee2.name=employee.name; this.employee=employee2; } public String getName() { return name; } public Employee getEmployee(){ return this.employee; } }
The output:
Employee name before modification: Prasanna Employee name after modification: Prasanna
Now its working as required.
You can also return a clone of the object from getEmployee() method, but for that your derived class should have implemented the Clone interface. We assume the class has not implemented it, if it does then our life would be little easy.