Immutable class in java
In this article, we will understand the meaning of immutable class in java, how to create custom immutable class with example.
Immutable meaning
Literal meaning of immutable is “not changing” or “unable to be changed”. When relating this word to a java object, it means an object whose state cannot be changed once the object is created.
State of an object is composed of its fields(or instance variables). In simple terms,
an immutable object is one whose field values cannot be changed after the object is created.
Class of such an object is an immutable class.
Use of Immutable Class
Instances of immutable class are non-changeable in terms of their state. This characteristic makes them ideal to be used :
- In caching. Load these objects with static data which is required by the application and use them across.
Since they are immutable, the data they hold will never be modified and you will get consistent data across the application. - In multi-thread applications. Objects, in such applications are shared by many threads and you would not want data of one thread to be manipulated by any other thread.
Thus, immutable objects would be of great help since its data can never be manipulated.
In order to create a custom immutable class in java, certain rules need to be followed. They are outlined below :
- Make the class final : This is required so that the class can not be extended and modified by exposing methods to change the state of its object.
- Make all the fields private : This is required so that the fields cannot be accessed from outside the class for modification.
- Make the fields final : This is done so that the fields are initialized only once from inside the constructor only.
- Remove all setter methods : So that field values are not modified from outside the class.
- If the class has any other object as a field(or instance variable) and this object is mutable, then make a copy of this object in the constructor before assigning it to the corresponding field of immutable class.
- If the class has a mutable object as a field(or instance variable), make sure to return a deep copy of this object from the getter method of this field.
Immutable class example
An example of immutable class is shown below.
public final class Employee { private final String name; private final String id; /** * Constructor * @param empName * @param eid */ public Employee(String empName, String eid) { name = empName; id = eid; } /** * Getter methods */ public String getName() { return name; } public String getId() { return id; } }
Above class represents an Employee with name
and id
fields.
Note that both its fields are final
and private
with no setter method. Thus, their value can only be set once through the constructor at the time of creation of Employee object.
Let’s say, a new field is added to our
Employee
class. This field is of class EmployeeDetail as given below.
public class EmployeeDetail { private int age; private double height; private String address; /** * Getter and Setter methods */ public int getAge() { return age; } public void setAge(int age) { this.age = age; } public public double getHeight() { return height; } public void setHeight(double height) { this.height = height; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
This class is not immutable. All its fields have a public
setter method and their values can be easily changed externally.
Now, a field of this type is added to our Employee
class. Modified version of Employee
class is given below.
public final class Employee { private final String name; private final String id; private final EmployeeDetail details; /** * Constructor * @param empName * @param eid * @param empHeight */ public Employee(String empName, String eid, EmployeeDetail empDetails) { name = empName; id = eid; details = empDetails; } /** * Getter methods */ public String getName() { return name; } public String getId() { return id; } public EmployeeDetail getDetails() { return details; } }
This field is also final
and has no public
setter method.
In the above section of creating immutable class, there were points 5 and 6 that stated :
- If an immutable class has a mutable object, then its copy must be assigned to the field in the constructor.
- If an immutable class has a mutable object, then its copy should be returned from the getter method of this field.
But these points are not followed in the updated Employee
class above since we are not creating a copy of EmployeeDetail in constructor of Employee
and not returning a copy of EmployeeDetail from getDetails
method.
Well Yes, they are not followed and that is why Employee
class is no more immutable.
Have a look at the below code snippet.
public static void main(String[] args) { // create EmployeeDetail object EmployeeDetail details = new EmployeeDetail(); details.setAddress("California"); details.setAge(25); details.setHeight(5.7); // create employee object using these details Employee emp = new Employee("Abc", "00410", details); // print height System.out.println("Employee height is " + emp.getDetails().getHeight()); // change height of employee details object details.setHeight(6.0); // again print height System.out.println("Employee height is " + emp.getDetails().getHeight()); }
Output will be
Employee height is 5.7
Employee height is 6.0
You can see that the class is no longer immutable since the height is modified.
Above problem can be solved by following point no 5, that is, assigning a copy of mutable object field in the constructor of immutable class.
Modified constructor is shown below.
public Employee(String empName, String eid, EmployeeDetail empDetails) { name = empName; id = eid; // create a copy of the mutable field EmployeeDetail copyObject = new EmployeeDetail(); // populate it with actual object values copyObject.setAddress(empDetails.getAddress()); copyObject.setAge(empDetails.getAge()); copyObject.setHeight(empDetails.getHeight()); // assign the copy object to the mutable field details = copyObject; }
Now running the same code which creates Employee object and then changes the height of the employee will give the following output.
Employee height is 5.7
Employee height is 5.7
This is because we have assigned EmployeeDetail object populated with initial values to the Employee
object once during its creation.
Now changing the EmployeeDetail will have no effect on Employee
object since it has its own copy of EmployeeDetail.
Returning mutable object from immutable class
Our Employee class returns an object of EmployeeDetail from its getDetails
method. This is the same object that is created from the constructor of Employee class. Remember that EmployeeDetail is not immutable.
Let’s understand what is the impact returning a mutable object from an immutable class.
Have a look at below code.
public static void main(String[] args) { // create a details object EmployeeDetail details = new EmployeeDetail(); details.setAddress("California"); details.setAge(25); details.setHeight(5.7); // create employee object using these details Employee emp = new Employee("Abc", "00410", details); // print height System.out.println("Employee height is " + emp.getDetails().getHeight()); // get details object from immutable object and change its height emp.getDetails().setHeight(6.0); // again print height System.out.println("Employee height is " + emp.getDetails().getHeight()); }
Output of this code will be
Employee height is 5.7
Employee height is 6.0
Again the class is not immutable since the height is changed
Reason is that the above code gets a mutable EmployeeDetail object from getDetails
method. When its value is modified using a setter method, this, in turn changes the actual immutable object.
This problem can be corrected by making a copy of the mutable object and returning it from its getter method from immutable class as shown below.
public EmployeeDetail getDetails() { // create a copy of the mutable field EmployeeDetail copyObject = new EmployeeDetail(); // populate it with actual object values copyObject.setAddress(details.getAddress()); copyObject.setAge(details.getAge()); copyObject.setHeight(details.getHeight()); return copyObject; }
Running the above code again will now print
Employee height is 5.7
Employee height is 5.7
With all the above changes incorporated, now the Employee
class is immutable.
All its primitive fields are final
and cannot be changed while any attempt to modify the mutable object field will always create a new object with actual values.
Immutable classes in Java Api
Java has many builtin classes which are immutable. You cannot change the state of their objects once created and if you are able to change, then you will be getting a new object with its contents copied from actual object.
Following are some immutable classes from Java api.
java.lang.String
Wrapper classes of primitives such as java.lang.Integer, java.lang.Boolean, java.lang.Double etc.
java.io.File
java.util.Date
java.util.Locale
java.util.UUID
java.net.URL
java.math.BigInteger
java.math.BigDecimal and many more…
Hope this post enhanced your knowledge about immutable classes. Do not forget to click the clap button below.