How to create an immutable class which has mutable instance object?

Serdar A.
3 min readNov 13, 2024

--

Hi everyone;

Sometimes you need an immutable class in your app. Maybe you want to convert your mutable class to immutable class.

Firstly;

Why we need a immutable class or what is advantages of immutable class?

  • Thread-safe object
  • Cacheable object

How to do an immutable class?

  • immutable class must be final
  • all properties must be private (access control)
  • all properties must be final (can not change)
  • using any setter method for properties

For example an immutable class

public final class Employee {         

private final String name;
private final Integer age;

public Employee(String name, Integer age) {
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public Integer getAge() {
return age;
}

@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
  • final class
  • private final properties
  • no setter method

Now we add a mutable class instance to immutable class

For example a mutable class (Address)

public class Address {

private String city;
private String state;

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

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;
}

@Override
public String toString() {
return "Address{" +
"city='" + city + '\'' +
", state='" + state + '\'' +
'}';
}
}

And we add this instance object (Address) to immutable class (Employee)

public final class Employee {

private final String name;
private final Integer age;
private final Address address; //mutable object

public Employee(String name, Integer age, Address address) {
this.name = name;
this.age = age;
Address cloneAddress = new Address(address.getCity(),address.getState()); // deep copy
this.address = cloneAddress;
}

public String getName() {
return name;
}

public Integer getAge() {
return age;
}

public Address getAddress() {
return new Address(address.getCity(),address.getState());
}

@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
", address=" + address +
'}';
}
}

Did you notice that how define mutable class inside constructor method and getter method of mutable class?

Firstly test this immutable class

public class TestClass{

public static void main(String[] args) {
Address address = new Address("city1","state1");
Employee employee = new Employee("employee1",20,address);

System.out.println(employee);

//after change
address.setCity("city2");
address.setState("state2");

System.out.println(employee);

Address address2 = employee.getAddress();
//after change other way
address2.setCity("city3");
address2.setState("state3");

System.out.println(employee);
}
}

Output is :

Employee{name='employee1', age=20, address=Address{city='city1', state='state1'}}
Employee{name='employee1', age=20, address=Address{city='city1', state='state1'}}
Employee{name='employee1', age=20, address=Address{city='city1', state='state1'}}

You can see mutable class in immutable class can not change values.

But if I change our class structure like below

public final class Employee {

private final String name;
private final Integer age;

private final Address address; //mutable object

public Employee(String name, Integer age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}

public String getName() {
return name;
}

public Integer getAge() {
return age;
}

public Address getAddress() {
return address;
}

@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
", address=" + address +
'}';
}
}

when run code example, output is :

Employee{name='employee1', age=20, address=Address{city='city1', state='state1'}}
Employee{name='employee1', age=20, address=Address{city='city2', state='state2'}}
Employee{name='employee1', age=20, address=Address{city='city3', state='state3'}}

In actually, because of changing of mutable class our immutable class changed.

What I did?

  1. In consturctor of immutable class, I did deep copy :
Address cloneAddress = new Address(address.getCity(),address.getState()); // deep copy
this.address = cloneAddress;

2. In getter method of mutable class inside immutable class :

return new Address(address.getCity(),address.getState());

I hope, it was useful for you.

Thanx to reading.

--

--

Serdar A.
Serdar A.

Written by Serdar A.

Senior Software Developer & Architect at Havelsan Github: https://github.com/serdaralkancode #Java & #Spring & #BigData & #React & #Microservice