How to create Custom Immutable Class in Java

Introduction

In this tutorial I am going to show you how to create a custom immutable class in Java programming language. Immutable objects are those objects whose states cannot be changed. For example, you can change the state of an object by changing its attribute’s or field’s value. String is an example of the immutable class in Java. There are other examples of immutable class in Java, such as, all primitive wrapper classes are immutable classes – Integer, Byte, Long, Float, Double, Character, Boolean, Short. In Java 8, the Date Time API made all classes to be immutable, for example, LocalDate, LocalTime, LocalDateTime, etc.

When you have the requirement to make your class instance immutable then you should have a strategic plan or sophisticated analysis. You may have a good reason why instances of the class never change after construction. There are some simple rules for creating immutable class in Java and it may not happen that all classes classified as immutable will follow these rules.

Steps to create Immutable Objects

The following rules define a simple strategy for creating immutable objects:

  1. You shouldn’t allow your class to be sub-classed for overriding the methods. The simplest way to do this is make the class final. A more elegant way is to create a private constructor and construct the objects or instances in factory methods.
  2. Make all fields or attributes private and final.
  3. Do not provide any setter methods for the fields:
    • Initialize all fields through constructor or in the factory method
  4. If the instance fields include references to the mutable objects, don’t allow those objects to be changed:
    • Don’t provide methods that modify mutable objects
    • Don’t share references to the mutable objects. Never store references to external, mutable objects passed to the constructor; if necessary, create copies, and store references to the copies. Similarly, create copies of your internal mutable objects when necessary to avoid returning the originals in your methods

Advantages of Immutable Objects

There are two main reasons you may want to make your objects immutable:

  1. Immutable objects are simpler as once they are created there is no way to change their state. So there is no side effect on it as any possible modification method will create and return a new instance.
  2. Immutable objects are inherently thread-safe. They can be easily used in multi-threaded environment and they cannot be corrupted by multiple threads accessing them concurrently.
  3. Immutable objects do not change their value, you can easily cache them because their value won’t change.
  4. Immutable objects are good candidate for hash keys because their hashcode can be cached and reused for better performance. That’s why it is recommeded to use as a key in HashMap.

Example of Object’s State Change

Here I am going to show a simple example how object’s state gets changed. Consider the following class:

public class ColorRGB {

    private int red;
    private int green;
    private int blue;
    private String name;

    public ColorRGB(int red, int green, int blue, String name) {
	this.red = red;
	this.green = green;
	this.blue = blue;
	this.name = name;
    }
	
    public void set(int red, int green, int blue, String name) {
	this.red = red;
	this.green = green;
	this.blue = blue;
	this.name = name;
    }

    public int getRGB() {
        return ((red << 16) | (green << 8) | blue);
    }

    public String getName() {
        return name;
    }

}

In the above class I have defined a method set() that sets the value for red, Green and Blue and name of the color.

Let’s suppose, a thread executes the following code:

ColorRGB color = new ColorRGB(0, 0, 0, "Black");

int myColorValue = color.getRGB(); //Statement 1
String myColorName = color.getName(); //Statement 2

If another thread invokes color.set() after Statement 1 but before Statement 2, the value of myColorValue won’t match the value of myColorName. So here is the point how your object’s state get changed. You could have setter method for individual field instead of set() method to set all fields at once.

Or you might have the following code snippets for changing the object’s state:

ColorRGB color = new ColorRGB(0, 0, 0, "Black");

System.out.println("Black color ref: " + color);

color.set(220, 20, 60, "Crimson"); // Change state of object

System.out.println("Crimson color ref: " + color);

int myColorValue = color.getRGB(); // Statement 1
String myColorName = color.getName(); // Statement 2

System.out.println("myColorValue: " + myColorValue + ", myColorName: " + myColorName);

The above code snippets will give you the following output:

Black color ref: com.roytuts.java.custom.immutable.clasz.ColorRGB@4517d9a3
Crimson color ref: com.roytuts.java.custom.immutable.clasz.ColorRGB@4517d9a3
myColorValue: 14423100, myColorName: Crimson

If you see the object reference value then you will notice they are pointing to the same object. So you have changed the state of the same object.

Immutable Class/Object

Now I am going to make the above class as an immutable.

package com.roytuts.java.custom.immutable.clasz;

public final class ImmutableColorRGB {

	private final int red;
	private final int green;
	private final int blue;
	private final String name;

	public ImmutableColorRGB(int red, int green, int blue, String name) {
		this.red = red;
		this.green = green;
		this.blue = blue;
		this.name = name;
	}

	public int getRGB() {
		return ((red << 16) | (green << 8) | blue);
	}

	public String getName() {
		return name;
	}

}

In the above class, you see that I have made all instance variables as private and final. I removed the set() method to prevent any modification to the state of the object.

Now create instance to test the above program by executing the following code snippets:

ImmutableColorRGB color = new ImmutableColorRGB(0, 0, 0, "Black");

System.out.println("Black color ref: " + color);

color = new ImmutableColorRGB(220, 20, 60, "Crimson");

System.out.println("Crimson color ref: " + color);

int myColorValue = color.getRGB(); // Statement 1
String myColorName = color.getName(); // Statement 2

System.out.println("myColorValue: " + myColorValue + ", myColorName: " + myColorName);

Executing the above code snippets you will get the following output:

Black color ref: com.roytuts.java.custom.immutable.clasz.ImmutableColorRGB@4517d9a3
Crimson color ref: com.roytuts.java.custom.immutable.clasz.ImmutableColorRGB@372f7a8d
myColorValue: 14423100, myColorName: Crimson

From the above output it is clear that any modification to the state of object creates a new instance because object reference values for two colors are different.

Using Private Constructor and Factory method in Immutable Object

Now I will show you a more sophisticated way to create immutable objects using private constructor and factory method in the class.

I will modify the above class to introduce private constructor instead of public constructor and add a factory method to initialize the values for RGB.

package com.roytuts.java.custom.immutable.clasz;

public final class ImmutableFactoryMethodColorRGB {

	private int red;
	private int green;
	private int blue;
	private String name;

	private ImmutableFactoryMethodColorRGB(int red, int green, int blue, String name) {
		this.red = red;
		this.green = green;
		this.blue = blue;
		this.name = name;
	}

	public static ImmutableFactoryMethodColorRGB instance(int red, int green, int blue, String name) {
		return new ImmutableFactoryMethodColorRGB(red, green, blue, name);
	}

	public int getRGB() {
		return ((red << 16) | (green << 8) | blue);
	}

	public String getName() {
		return name;
	}

}

Now create instance to test the above program by executing the following code snippets:

ImmutableFactoryMethodColorRGB color = ImmutableFactoryMethodColorRGB.instance(0, 0, 0, "Black");

System.out.println("Black color ref: " + color);

color = ImmutableFactoryMethodColorRGB.instance(220, 20, 60, "Crimson");

System.out.println("Crimson color ref: " + color);

int myColorValue = color.getRGB(); // Statement 1
String myColorName = color.getName(); // Statement 2

System.out.println("myColorValue: " + myColorValue + ", myColorName: " + myColorName);

Running the above code snippets will give you the same output as you have got from the above immutable class ImmutableColorRGB.

Black color ref: com.roytuts.java.custom.immutable.clasz.ImmutableFactoryMethodColorRGB@4517d9a3
Crimson color ref: com.roytuts.java.custom.immutable.clasz.ImmutableFactoryMethodColorRGB@372f7a8d
myColorValue: 14423100, myColorName: Crimson

Mutable Object in Immutable Class/Object

Now I will show you how to use mutable instance in immutable class. Let’s say you need to store a person’s address into a separate class. So you need to have two classes – Person and Address.

The Address class is given as follows:

package com.roytuts.java.custom.immutable.clasz;

public class Address implements Cloneable {

	private String city;
	private String country;

	public Address(String city, String country) {
		this.city = city;
		this.country = country;
	}

	public String getCity() {
		return city;
	}

	public void setCity(String city) {
		this.city = city;
	}

	public String getCountry() {
		return country;
	}

	public void setCountry(String country) {
		this.country = country;
	}

        @Override
	public String toString() {
		return "Address [city=" + city + ", country=" + country + "]";
	}

}

The Person class is given as follows:

package com.roytuts.java.custom.immutable.clasz;

public final class Person {

	private final String name;
	private final Address address;

	public Person(String name, Address address) {
		this.name = name;
		this.address = address;
	}

	public String getName() {
		return name;
	}

        public Address getAddress() {
		return address;
	}

}

So the above Person class is immutable and you have the mutable instance Address object in Person class.

In the above Address class I have created getters and setters and the class Person having this Address object is no more immutable. Immutability in Person class can easily be breaked.

Let’s check how to break Person class.

package com.roytuts.java.custom.immutable.clasz;

public class PersonImmutabilityTest {

	public static void main(String[] args) {
		Person person = new Person("Soumitra", new Address("City", "Country"));

		Address address = person.getAddress();
		System.out.println(address);

		address.setCity("City1");
		address.setCountry("Country1");
		System.out.println("After: " + address);
	}

}

So you will see the following output from the above code:

Address [city=City, country=Country]
After: Address [city=City1, country=Country1]

Here I got the reference of address object using getAddress() method and stored into a new local variable. This reference still points to the same address instance. Then I changed the value of city and country fields of the address instance and it updated the address instance used by Person object. Hence when I tried to get the address of the person, it gave me the updated address. So Person class is no longer immutable class.

Now how can you make the Person class immutable? There are two options – one is to remove setter methods from Address class and you can create child class that will extend Address class. First option may not be possible in case the class is from third party library or jar. Then you have only second option. Now create the following MyAddress that extends Address class.

package com.roytuts.java.custom.immutable.clasz;

public class MyAddress extends Address implements Cloneable {

	public MyAddress(String city, String country) {
		super(city, country);
	}

	@Override
	public void setCity(String city) {
		throw new UnsupportedOperationException();
	}

	@Override
	public void setCountry(String country) {
		throw new UnsupportedOperationException();
	}

	@Override
	public Object clone() {
		try {
			return super.clone();
		} catch (Exception e) {
			return null;
		}
	}
}

Update the Person class as below:

package com.roytuts.java.custom.immutable.clasz;

public final class Person {

	private final String name;
	private final MyAddress address;

	public Person(String name, MyAddress address) {
		this.name = name;
		this.address = address;
	}

	public String getName() {
		return name;
	}

	public MyAddress getAddress() {
		return (MyAddress) address.clone();
	}

}

Update your main class also:

package com.roytuts.java.custom.immutable.clasz;

public class PersonImmutabilityTest {

	public static void main(String[] args) {
		Person person = new Person("Soumitra", new MyAddress("City", "Country"));

		Address address = person.getAddress();
		System.out.println(address);

		address.setCity("City1");
		address.setCountry("Country1");
		System.out.println("After: " + address);
	}

}

Now executing the above class you will see the following output:

Address [city=City, country=Country]
Exception in thread "main" java.lang.UnsupportedOperationException
	at com.roytuts.java.custom.immutable.clasz.MyAddress.setCity(MyAddress.java:11)
	at com.roytuts.java.custom.immutable.clasz.PersonImmutabilityTest.main(PersonImmutabilityTest.java:12)

So we solved the problem but what happens if there are more setters methods add to Address class in future then we have to override in future also. Therefore you can return the clone object for the Address instance. In this case even if third party changes anything to the original Address, it is not going the affect the original Address object in the Person object. For this scenario Address class has to implement Cloneable interface and has the clone() method implemented. For most of the cases third party library does it.

The Address class looks similar to the below code:

package com.roytuts.java.custom.immutable.clasz;

public class Address implements Cloneable {

	private String city;
	private String country;

	public Address(String city, String country) {
		this.city = city;
		this.country = country;
	}

	public String getCity() {
		return city;
	}

	public void setCity(String city) {
		this.city = city;
	}

	public String getCountry() {
		return country;
	}

	public void setCountry(String country) {
		this.country = country;
	}

	@Override
	public Object clone() {
		try {
			return super.clone();
		} catch (Exception e) {
			return null;
		}
	}
	
	@Override
	public String toString() {
		return "Address [city=" + city + ", country=" + country + "]";
	}

}

The Person class has the following code:

package com.roytuts.java.custom.immutable.clasz;

public final class Person {

	private final String name;
	private final Address address;

	public Person(String name, Address address) {
		this.name = name;
		this.address = address;
	}

	public String getName() {
		return name;
	}

	public Address getAddress() {
		return (Address) address.clone();
	}

}

Test the immutability of the Person class:

package com.roytuts.java.custom.immutable.clasz;

public class PersonImmutabilityTest {

	public static void main(String[] args) {
		Person person = new Person("Soumitra", new Address("City", "Country"));

		Address address = person.getAddress();
		System.out.println(address);

		address.setCity("City1");
		address.setCountry("Country1");
		
		System.out.println("person.getAddress(): " + person.getAddress());
		
		System.out.println("After: " + address);
	}

}

Now running the above class you will get the following output:

Address [city=City, country=Country]
person.getAddress(): Address [city=City, country=Country]
After: Address [city=City1, country=Country1]

From the above output you see that address in the Person object has not been changed.

Now what if your Address class does not implement Cloneable interface and you do not have idea also whether Address class implements or not. Then you can go with the deep copy of the Address object and return it.

Now your Address class looks similar to the below code:

package com.roytuts.java.custom.immutable.clasz;

public class Address {

	private String city;
	private String country;

	public Address(String city, String country) {
		this.city = city;
		this.country = country;
	}

	public String getCity() {
		return city;
	}

	public void setCity(String city) {
		this.city = city;
	}

	public String getCountry() {
		return country;
	}

	public void setCountry(String country) {
		this.country = country;
	}
	
	@Override
	public String toString() {
		return "Address [city=" + city + ", country=" + country + "]";
	}

}

Your Person class looks similar to the below code:

package com.roytuts.java.custom.immutable.clasz;

public final class Person {

	private final String name;
	private final Address address;
	
	public Person(String name, Address address) {
		this.name = name;
		this.address = new Address(address.getCity(), address.getCountry());
	}

	public String getName() {
		return name;
	}
	
	public Address getAddress() {
		Address addr = new Address(address.getCity(), address.getCountry());
		return addr;
	}
	
}

Your main class looks same as you did for clone():

package com.roytuts.java.custom.immutable.clasz;

public class PersonImmutabilityTest {

	public static void main(String[] args) {
		Person person = new Person("Soumitra", new Address("City", "Country"));

		Address address = person.getAddress();
		System.out.println(address);

		address.setCity("City1");
		address.setCountry("Country1");
		
		System.out.println("person.getAddress(): " + person.getAddress());
		
		System.out.println("After: " + address);
	}

}

Now you will get the same output as shown below:

Address [city=City, country=Country]
person.getAddress(): Address [city=City, country=Country]
After: Address [city=City1, country=Country1]

Hope you have got idea on how to create immutable class in Java and how to handle reference to the mutable instance.

Source Code

Download

Leave a Comment