Immutable objects are by default thread safe.
Steps for creating a immutable class:
- Make your class final :
If you make your class final, no class will be able to extend it, hence will not be able override methods of this class. - Declare all instance variable with private and final :
If you make instance variable private, no outside class will be able to access instance variables and if you make them final, you can not change it. - Say no to setter methods :
Don’t create setter method for any instance variables, hence there will be no explicit way to change state of instance variables. - Initialize all variables in constructor :
You can initialize variables in constructor. You need to take special care while working with mutable object. You need to do deep copy in case of imutable objects. - Perform cloning of mutable objects while returning from getter method:
If you return clone of object from getter method, it won’t return original object, so your original object will remain intact. I will explain this more in later part of this tutorial.
Read : Why String is immutable in java
Lets understand immutable class with a very simple example:
Lets create a simple class called Country.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 34 35 36 37 38 39 40 |
public final class Country{ private final String countryName; private final ArrayList listOfStates; public Country(String countryName,ArrayList listOfStates) { super(); this.countryName = countryName; this.listOfStates=listOfStates; } public String getCountryName() { return countryName; } public ArrayList getListOfStates() { return listOfStates; } public static void main(String args[]) { ArrayList listOfStates=new ArrayList(); listOfStates.add("Madhya Pradesh"); listOfStates.add("Maharastra"); listOfStates.add("Gujrat"); Country country=new Country("India",listOfStates); System.out.println("Country : "+country.getCountryName()); System.out.println("List of states : "+country.getListOfStates()); // It will be added to the list because we did not use clone in getListOfStates country.getListOfStates().add("Kerala"); // It will be added to the list because we did not use deep copy in constructor listOfStates.add("Rajasthan"); System.out.println("Updated List of states : "+country.getListOfStates()); } } |
When you run the program, you will get below output:
1 2 3 4 5 |
Country : India List of states : [Madhya Pradesh, Maharastra, Gujrat] Updated List of states : [Madhya Pradesh, Maharastra, Gujrat, Kerala, Rajasthan] |
Above class is not immutable. There are two reasons for it :
- We did not use clone in getListOfStates() method, so we are able to add “Kerala” to the listOfStates.
- We did not do deep copy for listOfStates , so we are able to add “Rajasthan” to the list.
1 2 3 4 5 |
public ArrayList getListOfStates() { return (ArrayList) listOfStates.clone(); } |
1 2 3 4 5 |
Country : India List of states : [Madhya Pradesh, Maharastra, Gujrat] Updated List of states : [Madhya Pradesh, Maharastra, Gujrat, Rajasthan] |
We are one step closed to immutable class now.
Lets change constructor to make deep copy of listOfStates object.
1 2 3 4 5 6 7 8 9 10 11 12 |
public Country(String countryName, ArrayList listOfStates) { super(); this.countryName = countryName; ArrayList tempList = new ArrayList(); for (int i = 0; i < listOfStates.size(); i++) { tempList.add(listOfStates.get(i)); } this.listOfStates = tempList; } |
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
package org.arpit.java2blog.bean; import java.util.ArrayList; public final class Country { // declared private final instance variable private final String countryName; // Mutable object private final ArrayList listOfStates; public Country(String countryName, ArrayList listOfStates) { super(); this.countryName = countryName; // Creating deep copy for mutable object ArrayList tempList = new ArrayList(); for (int i = 0; i < listOfStates.size(); i++) { tempList.add(listOfStates.get(i)); } this.listOfStates = tempList; } public String getCountryName() { // Do not need to do cloning as it is immutable object return countryName; } public ArrayList getListOfStates() { // Returning cloned object return (ArrayList) listOfStates.clone(); } public static void main(String args[]) { ArrayList listOfStates = new ArrayList(); listOfStates.add("Madhya Pradesh"); listOfStates.add("Maharastra"); listOfStates.add("Gujrat"); String countryName="India"; Country country = new Country(countryName, listOfStates); System.out.println("Country : " + country.getCountryName()); // Lets try to change local variable countryName countryName="China"; System.out.println("Updated Country : " + country.getCountryName()); System.out.println("List of states : " + country.getListOfStates()); // It will not be added to the list because we are using clone in // getListOfStates country.getListOfStates().add("Kerala"); // It will not be added to the list because we are using deep copy in // constructor listOfStates.add("Rajasthan"); System.out.println("Updated List of states : " + country.getListOfStates()); } } |
1 2 3 4 5 6 |
Country : India Updated Country : India List of states : [Madhya Pradesh, Maharastra, Gujrat] Updated List of states : [Madhya Pradesh, Maharastra, Gujrat] |
I hope it will help you to create immutable class in java.