Thursday, 10 August 2017

How to create immutable class in Java

Immutable class, as the name suggests, is a class whose object can’t be modified in anyway once created. Once an object of the immutable class is created and initialized the content of any field of that object can’t be changed in anyway and remains same throughout the life of the object. If there are methods that should modify the content in some way then a new object is created with the modified content and returned, original object does not change.

As example String in Java is an immutable class though there are methods with in String class like substring or replace which modify the created String object. You can see in the code of the String class that for all these type of methods a new string is created with the modified data and returned. Other examples of immutable classes in Java are all the wrapper classes for primitive types like Integer, Long etc. BigDecimal and BigInteger are also immutable classes.

How to create an immutable class

There are certain steps that are to be followed for creating an immutable class –

  1. Methods of the class should not be overridden by the subclasses. You can ensure that by making your class final.

  2. Make all fields final and private. If the field is of primitive type, then it’s value can’t be changed as it is final. If field is holding a reference to another object, then declaring that field as final means its reference can’t be changed.
    Having access modifier as private for the fields ensure that fields are not accessed outside the class.

  3. Initialize all the fields in a constructor.

  4. Don’t provide setter methods or any method that can change the state of the object. Only provide methods that can access the value (like getters).

  5. In case any of the fields of the class holds reference to a mutable object any change to those objects should also not be allowed, for that –
    • Make sure that there are no methods with in the class that can change those mutable objects (change any of the field content).
    • Don’t share reference of the mutable object, if any of the methods of your class return the reference of the mutable object then its content can be changed.
    • If reference must be returned create copies of your internal mutable objects and return those copies rather than the original object. The copy you are creating of the internal mutable object must be a deep copy not a shallow copy.

In case you are wondering why such elaborate procedure for the fields holding references when fields are already final and references can’t be changed. Remember that even if reference cannot be changed you can still change the content of the fields with in a mutable object which goes against what you want to achieve by making your class immutable.

Example Code

In the example there are two classes. Address class is the mutable class whose object is there in the immutable class ImmutableEmployee.

Address.java

public class Address {
 private String addressLine1;
 private String addressLine2;
 private String city;
 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;
 }
 public String getCity() {
  return city;
 }
 public void setCity(String city) {
  this.city = city;
 }
 public String toString() {
  return "AddressLine1 " + addressLine1 + " AddressLine2 " + addressLine2 + " City " + city;
 }
}

ImmutableEmployee

public class ImmutableEmployee {
 private final String name;
 private final int age;
 private final Address empAddress;
 ImmutableEmployee(String name, int age, String add1, String add2, String city){
  this.name = name;
  this.age = age;
  // don't pass reference around create a new object with in constructor
  empAddress = new Address();
  empAddress.setAddressLine1(add1);
  empAddress.setAddressLine2(add2);
  empAddress.setCity(city);
 }
 
 public String getName() {
  return name;
 }
 public int getAge() {
  return age;
 }
 
 public Address getEmpAddress() {
  Address adr = new Address();
  adr.setAddressLine1(empAddress.getAddressLine1());
  adr.setAddressLine2(empAddress.getAddressLine2());
  adr.setCity(empAddress.getCity());
  return adr;
  /*return empAddress;*/
 }
 
 public String toString() {
  return "Name " + name + " Age " + age + " Address " + empAddress;
 }

 public static void main(String[] args) {
  ImmutableEmployee ie = new ImmutableEmployee("Jack", 32, "34, Hunter's Glen", "Pine Street", "Brooklyn");
  System.out.println("Employee information " + ie);
  Address adr = ie.getEmpAddress();
  adr.setCity("London");
  System.out.println("Employee information " + ie);
 }

}

Output

Employee information Name Jack Age 32 Address AddressLine1 34, Hunter's Glen AddressLine2 Pine Street City Brooklyn
Employee information Name Jack Age 32 Address AddressLine1 34, Hunter's Glen AddressLine2 Pine Street City Brooklyn

Here notice that in the line Address adr = ie.getEmpAddress(); I get the address object and change the city in the object but that change is not reflected in the Employee object because getEmpAddress() method creates and returns a new Address object.

You can change that method to return the original Address object, in that case your getEmpAddress() method will look like this –

 public Address getEmpAddress() {
  /*Address adr = new Address();
  adr.setAddressLine1(empAddress.getAddressLine1());
  adr.setAddressLine2(empAddress.getAddressLine2());
  adr.setCity(empAddress.getCity());
  return adr;*/
  return empAddress;
 }

Now if you execute the code and see the output -

Output

Employee information Name Jack Age 32 Address AddressLine1 34, Hunter's Glen AddressLine2 Pine Street City Brooklyn
Employee information Name Jack Age 32 Address AddressLine1 34, Hunter's Glen AddressLine2 Pine Street City London

You can see that Employee information is changed, you are now able to change the content of the Employee class object which should not be happening as it’s an immutable class. That’s why the point as mentioned above “If reference must be returned create copies of your internal mutable objects and return those copies rather than the original object.” is important.

If your class has any mutable object or using any of the modifiable collections like ArrayList, HashSet, HashMap same steps are to be followed.

Advantages of using immutable class

  1. One of the advantage of an immutable class is it is thread safe and its object can be shared among many threads without any fear of content of the original object getting changed. Refer String and thread safety to have more clarity on this point.

  2. Another advantage is you can cache immutable class objects as you know content won’t change once the object is created so no fear of dirty read. This ability to cache and reuse reduces overhead.

  3. If you ever wondered why do you see mostly String as a key for HashMap, yes, it is mostly because of String being immutable. So, if you are somehow required to use your own custom object as key and want to make sure that the object is not changed by any means (like any other thread) making that class immutable will be an option.

Disadvantages of using immutable class

  1. If the second point in the advantages of using immutable class has impressed you to make as many immutable classes as possible just take a pause and think of the disadvantage too. Any get method where you are supposed to return the immutable class object or any field that holds a reference to a mutable object you do need to create a new object every time, copy the content from the original object and then return it. Which results in creation of many new objects and performing deep copies which again is expensive.

That's all for this topic How to create an immutable class in Java. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. How to create deadlock in Java multi-threading - Java Program
  2. Print odd-even numbers using threads and wait-notify
  3. How to remove duplicate elements from an array - Java Program
  4. String and thread-safety in Java
  5. String comparison in Java

You may also like -

>>>Go to Java Basics page

No comments:

Post a Comment