Immutable class in java

Immutable Object: A object is know as immutable if it’s state can not be changed over-time or we can say after object creation.

Need of Immutable Classes

In current days, most of the applications are running into multi-threading environment which results into concurrent modification problems.

Popular Immutable classes in java:

All wrapper classes (java.lang.Integer, java.lang.Byte, java.lang.Character, java.lang.Short, java.lang.Boolean, java.lang.Long, java.lang.Double, java.lang.Float), String class, java.lang.StackTraceElement, java.math.BigInteger, java.math.BigDecimalall, java.io.File, java.awt.Font, java.awt.BasicStroke, java.awt.Color, java.awt.Cursor, java.util.Locale, java.util.UUID, java.net.URL, java.net.URI, java.net.Inet4Address, java.net.Inet6Address, java.net.InetSocketAddress, most subclasses of java.security.Permission except java.security.PermissionCollection and subclasses, all classes of java.time except DateTimeException and number of collection classes are immutable classes in java.

Example of String as an immutable class

public class Main {
 
    public static void main(String[] args) {
        String testString1 = "website";
        System.out.println("testString1: " + testString1);
        String testString2 = testString1 + ".com";
        System.out.println("testString2: " + testString2);
        System.out.println("testString1: " + testString1);
    }
}

Output

testString1: website
testString2: website.com 
testString1: website

Create Custom Immutable Class:

You can write your own immutable class, when creating immutable class just keep in mind that after creating an object of this class object can’t be modified. Any change in existing object result into new object.

  1. Make final class so that it cannot inherit.
  2. Object state is made up of its properties, declare all properties final. So that its properties value will remain constant.
  3. Object properties value can be set using setter methods, so only define getter methods for all properties.
  4. Always return a new class instance from the methods which can modify the state of the class.
  5. Use deep copy instead of shallow copy, while initializing the properties by constructor.
  6. In getter methods, always perform cloning and return the clone copy instead of actual object reference.

Simple Example of Custom Immutable Class:

ImmutableClassExample.java

/**
 * This program is used to create a immutable class.
 * @author w3spoint
 */
final class Student{
	//declare all properties final.
	final String rollNo;	
 
	public Student(String rollNo){
		this.rollNo = rollNo;
	}
 
	//only create getter method.
	public String getRollNo() {
		return rollNo;
	}
}
 
public class ImmutableClassExample {
	public static void main(String args[]){
		//creating Student object. 
		Student obj = new Student("MCA/07/06");
 
		System.out.println(obj.getRollNo());
	}
}

Output:

MCA/07/06

Now, Let’s move to some complex scenarios

Mutable Objects in Immutable Class

Here, we are creating Address class which is a mutable class and will use the Address objects into ImmutableEmployee class which is immutable.

Address.java:

public class Address {
	private String addressLine;
	private String city;
	private String state;
	private String pinCode;
 
	public String getAddressLine() {
		return addressLine;
	}
	public void setAddressLine(String addressLine) {
		this.addressLine = addressLine;
	}
	public String getCity() {
		return city;
	}
	public void setCity(String city) {
		this.city = city;
	}
	public String getState() {
		return state;
	}
	public void setState(String state) {
		this.state = state;
	}
	public String getPinCode() {
		return pinCode;
	}
	public void setPinCode(String pinCode) {
		this.pinCode = pinCode;
	}
 
}

ImmutableEmployee.java:

public class ImmutableEmployee {
	private final String name;
	private final long id;
	private final Address address;
 
	public ImmutableEmployee(String name, long id, Address address) {
		super();
		this.name = name;
		this.id = id;
		this.address = address;
	}
 
	public String getName() {
		return name;
	}
 
	public long getId() {
		return id;
	}
 
	public Address getAddress() {
		return address;
	}
 
}

MainTest.java:

public class MainTest {
 
    public static void main(String[] args) {   
        Address address = new Address();
        address.setAddressLine("Address line 1");
        address.setCity("Test City");
        address.setState("Test State");
        address.setPinCode("123456");
 
        ImmutableEmployee immutableEmployee = new ImmutableEmployee("Jai", 10, address);
        System.out.println(immutableEmployee.getName() + 
        		"'s city before modification: " + immutableEmployee.getAddress().getCity());
        address.setCity("Modified City");
        System.out.println(immutableEmployee.getName() + 
        		"'s city after modification: " + immutableEmployee.getAddress().getCity());
    } 
}

Output:

Jai's city before modification: Test City
Jai's city after modification: Modified City

As you can see in the output, Objects state is being changed if we modify the mutable object property of Immutable class object. So, as per #5, we will use deep copy instead of shallow copy when initializing the properties via constructor.

Constructor of ImmutableEmployee class will be:

public ImmutableEmployee(String name, long id, Address address) {
	super();
	this.name = name;
	this.id = id;
	Address cloneAddress = new Address();
	cloneAddress.setAddressLine(address.getAddressLine());
	cloneAddress.setCity(address.getCity());
	cloneAddress.setState(address.getState());
	cloneAddress.setPinCode(address.getPinCode());
	this.address = cloneAddress;
}

Output with above change:

Jai's city before modification: Test City
Jai's city after modification: Test City

Result seems good but our class in not fully immutable yet. Change MainTest class code with below code and see the result.
Updated MainTest.java

public class MainTest {
 
    public static void main(String[] args) {   
        Address address = new Address();
        address.setAddressLine("Address line 1");
        address.setCity("Test City");
        address.setState("Test State");
        address.setPinCode("123456");
 
        ImmutableEmployee immutableEmployee = new ImmutableEmployee("Jai", 10, address);
        System.out.println(immutableEmployee.getName() + 
        		"'s city before modification: " + immutableEmployee.getAddress().getCity());
        immutableEmployee.getAddress().setCity("Modified City");
        System.out.println(immutableEmployee.getName() + 
        		"'s city after modification: " + immutableEmployee.getAddress().getCity());
    } 
}

Output with above change:

Jai's city before modification: Test City
Jai's city after modification: Modified City

You can see, city is modified again. Now, according to #6 we have to perform cloning in getter method and have to return copy of mutable object instead of actual reference. Let’s update the getAddress method code of ImmutableEmployee class.

Updated getAddress() method

public Address getAddress() {
	Address cloneAddress = new Address();
	cloneAddress.setAddressLine(this.address.getAddressLine());
	cloneAddress.setCity(this.address.getCity());
	cloneAddress.setState(this.address.getState());
	cloneAddress.setPinCode(this.address.getPinCode());
	return cloneAddress;
}

Output with above change:

Jai's city before modification: Test City
Jai's city after modification: Test City

Now, our class is fully Immutable.

Advantages/Benefits of immutable class.

  1. Objects are thread safe by default.
  2. No need to synchronize immutable objects explicitly.

Disadvantages of immutable classes.

As discussed any change in immutable object result into a new object, hence result in unnecessary garbage.
 
Next Topic: toString() method in java with example.
Previous Topic: Substring in java with example.

 

Please Share