In this blog post we will discuss about one of the very important java interview question How can we maintain Immutability of a class with a mutable reference ?
To create an immutable class, you must follow the below steps?
- Make your class final, so that no other classes can extend it.
- Make all instance variables private & final, so that they’re initialized only once inside the constructor and never modified afterward.
- Provide only getter methods don’t provide setter methods.
- If the class holds a mutable object:
- You make sure to always return a clone or defensive copy of the field and never return the real object instance from getter method.
Let’s try to understand above points using an example
You can see How we made Employee.java class immutable
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 58 59 60 61 62 63 64 65 66 |
package com.main.model; import java.util.Date; import java.util.LinkedHashSet; import java.util.Set; /** * @author KK JavaTutorials * This class is Immutable */ public final class Employee { // Here id,name and email are already Immutable private final Integer id; private final String name; private final String email; /*As Date,Set<String> and Address are Mutable referfeces so returns clone or defensive copy from their getter method*/ private final Date dob; private final Set<String> skills; private final Address address; public Employee(Integer id, String name, String email, Date dob, Set<String> skills, Address address) { super(); this.id = id; this.name = name; this.email = email; this.dob = dob; this.skills = skills; this.address = address; } public Integer getId() { return id; } public String getName() { return name; } public String getEmail() { return email; } public Date getDob() { //Returns clone or defensive copy of Date return new Date(dob.getTime()); } public Set<String> getSkills() { //Returns clone or defensive copy of Set return new LinkedHashSet<>(skills); } public Address getAddress() { //Returns clone or defensive copy of Set Address empAddress = new Address(); empAddress.setStreet(address.getStreet()); empAddress.setZipCode(address.getZipCode()); empAddress.setAddressLine1(address.getAddressLine1()); empAddress.setAddressLine2(address.getAddressLine2()); return empAddress; } } |
Address is a Mutable class and it’s reference used by Employee class
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 |
package com.main.model; /** * @author KK JavaTutorials * This class is mutable */ public class Address { private String street; private Long zipCode; private String addressLine1; private String addressLine2; public String getStreet() { return street; } public void setStreet(String street) { this.street = street; } public Long getZipCode() { return zipCode; } public void setZipCode(Long zipCode) { this.zipCode = zipCode; } public String getAddressLine1() { return addressLine1; } public void setAddressLine1(String addressLine1) { this.addressLine1 = addressLine1; } public String getAddressLine2() { return addressLine2; } public void setAddressLine2(String addressLine2) { this.addressLine2 = addressLine2; } } |
MyUtil.java is a Utility class used to convert String into Date
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
package com.main.util; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; /** * @author KK JavaTutorials */ public class MyUtil { /** * @param Input date in String * @return Date instance * @throws ParseException throws exception if input string is not able to parse into Date */ public static Date stringToDate(String d) throws ParseException{ DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy"); Date date = dateFormat.parse(d); return date; } } |
Finally We have Client program which makes use of Immutable class
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 58 59 60 61 62 63 64 65 |
package com.main.kkjavatutorials; import java.text.ParseException; import java.util.Date; import java.util.LinkedHashSet; import java.util.Set; import com.main.model.Address; import com.main.model.Employee; import com.main.util.MyUtil; /** * @author KK JavaTutorials * Client program which used immutable class */ public class ClientTest { public static void main(String[] args) throws ParseException { Address address = new Address(); address.setStreet("Park Street"); address.setZipCode(1099209L); address.setAddressLine1("Adddress line 1"); address.setAddressLine2("Adddress line 2"); Set<String> skills= new LinkedHashSet<>(); skills.add("Spring"); skills.add("Hibernate"); Date dob = MyUtil.stringToDate("04/01/1980"); System.out.println("ID:"+employee.getId()); System.out.println("Name:"+employee.getName()); System.out.println("Email:"+employee.getEmail()); System.out.println("DOB:"+employee.getDob()); System.out.println("Skills:"+employee.getSkills()); System.out.println("Employee Address::::"); System.out.println("Street:"+address.getStreet()); System.out.println("ZipCode:"+address.getZipCode()); System.out.println("AddressLine1:"+address.getAddressLine1()); System.out.println("AddressLine2:"+address.getAddressLine2()); //Let's try to change state of this employee object System.out.println("------------------------------------------------------------"); //Try to modify Employee Address Street value employee.getAddress().setStreet("Orchid Street"); //Try to modify Employee DOB employee.getDob().setTime(new Date().getTime()); //Try to modify Emplyee skill sets employee.getSkills().add("Spring Boot"); System.out.println("ID:"+employee.getId()); System.out.println("Name:"+employee.getName()); System.out.println("Email:"+employee.getEmail()); System.out.println("DOB:"+employee.getDob()); System.out.println("Skills:"+employee.getSkills()); System.out.println("Employee Address::::"); System.out.println("Street:"+address.getStreet()); System.out.println("ZipCode:"+address.getZipCode()); System.out.println("AddressLine1:"+address.getAddressLine1()); System.out.println("AddressLine2:"+address.getAddressLine2()); } } |
Sample Output of above program:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
ID:109023 Name:Sean Murphy Email:sean.m@kkjava.com DOB:Fri Jan 04 00:00:00 IST 1980 Skills:[Spring, Hibernate] Employee Address:::: Street:Park Street ZipCode:1099209 AddressLine1:Adddress line 1 AddressLine2:Adddress line 2 ------------------------------------------------------------ ID:109023 Name:Sean Murphy Email:sean.m@kkjava.com DOB:Fri Jan 04 00:00:00 IST 1980 Skills:[Spring, Hibernate] Employee Address:::: Street:Park Street ZipCode:1099209 AddressLine1:Adddress line 1 AddressLine2:Adddress line 2 |
That’s all about When How can we maintain the Immutability of a class with a mutable reference?
You May Also Like:
What are common multi-threading issues faced by Java Developers?
What are different states of a Thread? What does those states tell us?
What happens when wait() & notify() methods are called?
What is difference between sleep(), yield() and wait() method?
What is difference between intrinsic synchronization and explicit locking using Lock?
What will happen when an exception arises from within a synchronized code block? Will lock be retained or released?
What is difference between Executor submit() and execute() method?
If you have any feedback or suggestion please feel free to drop in below comment box.
Great work Kishan…Really You Have done awesome work thank’s a lot…
Nice explanation