Wednesday, 5 June 2019

How to Create Custom Immutable Class with mutable object references in Java?

       We discussed in previous post about How to create the Immutable object in Java.  We discussed the all steps or rules we need to follow while creating immutable class in java. But if any field in the custom immutable class contains reference to any other mutable class then it will break immutable behavior. The reference class can be from imported jar files or any other imported application class and we can not access/control that mutable class. In this post, we will discuss how to make/convert mutable reference to immutable so that the complete class will become immutable.

      In the below example, Employee class is immutable but filed Address is mutable, it's another java class . 

Employee.java,
package com.practice;

public final class Employee {
 
        private final String empName;
        private final int age;
        private final Address address;
 
        public Employee(String name, int age, Address address) {
                super();
                this.empName = name;
                this.age = age;
                this.address = address;
        }

        public String getEmpName() {
                return empName;
        }

        public int getAge() {
                return age;
        }

         public Address getAddress() {
                return address;
         }

}

        In the above code, empName and age fields are immutable, we can not change the value but Address field it's reference of another java class and we can not have access or to modify the Address class.Here Address field is mutable, so we can restrict to modify the Address object/field in Employee class.

Address.java,
package com.practice;

public class Address implements Cloneable{
 
         public String addressType;
         public String address;
         public String city;
 
         public Address(String addressType, String address, String city) {
                   super();
                   this.addressType = addressType;
                   this.address = address;
                   this.city = city;
         }
         public String getAddressType() {
                   return addressType;
         }
         public void setAddressType(String addressType) {
                   this.addressType = addressType;
         }
         public String getAddress() {
                   return address;
         }
         public void setAddress(String address) {
                    this.address = address;
         }
         public String getCity() {
                    return city;
         }
         public void setCity(String city) {
                    this.city = city;
         }

         public Object clone() throws CloneNotSupportedException {  
                  return super.clone();  
         }

        @Override
        public String toString() {
                 return "Address Type - "+addressType+", address - "+address+", city - "+city;
        }
}

         In the below main class, you can create the Employee object using constructor, pass all parameters i.e empName, age and address. In the next line again we can set the addressType, address and city into the Address object. Now you try to access the address field from employee object it will get change. So in this case it will break the immutable behavior.

MainClass.java,
package com.practice;

public class MainClass {
 
         public static void main(String[] args) {
  
                   Employee emp = new Employee("Mahesh", 34, new Address("Home", "JP Nagar", "Bangalore"));
  
                   Address address = emp.getAddress();
  
                   System.out.println(address);
  
                   address.setAddress("Jayanagar");
                   address.setAddressType("Office");
                   address.setCity("Sangli");
  
                   System.out.println(emp.getAddress());  
         }

}

Output:-
Address Type - Home, address - JP Nagar, city - Bangalore
Address Type - Office, address - Jayanagar, city - Sangli


Solution : -

The best solution is to modify the getAddress method in employee class, and instead of returning original Address object, we will return deep cloned copy of that instance. But one condition Address class must implements cloneable interface.

Modified employee class is as follow,

Employee.java,
package com.practice;

public final class Employee {
 
         private final String empName;
         private final int age;
         private final Address address;
 
         public Employee(String name, int age, Address address) {
                  super();
                  this.empName = name;
                  this.age = age;
                  this.address = address;
         }

         public String getEmpName() {
                  return empName;
         }

         public int getAge() {
                  return age;
         }

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

}

Now we can check to execute main class,

MainClass.java,
package com.practice;

public class MainClass {
 
         public static void main(String[] args) {
  
                   Employee emp = new Employee("Mahesh", 34, new Address("Home", "JP Nagar", "Bangalore"));
  
                   Address address = emp.getAddress();
  
                   System.out.println(address);
  
                   address.setAddress("Jayanagar");
                   address.setAddressType("Office");
                   address.setCity("Sangli");
  
                   System.out.println(emp.getAddress());  
         }

}

Output:-
Address Type - Home, address - JP Nagar, city - Bangalore
Address Type - Home, address - JP Nagar, city - Bangalore


Related Posts:-
1) String Interview Questions and Answers
2) Why String is immutable or final in java?
3) Difference between Loose Coupling and Tight Coupling in Java With Examples.
4) Builder Design Pattern in Java
5) What is call by value and call by reference in Java ?

No comments:

Post a comment