Sunday 15 May 2016

How to create Immutable class

A class is said to be immutable, once you create an object for the class, you can’t change the fields of object. An immutable object is the one that will not change its internal state after creation. When dealing with concurrency, immutable objects are very useful, since you can’t change the state of immutable object, you can share the object between multiple threads without worrying.

In Effective Java, Joshua Bloch makes this compelling recommendation:

"Classes should be immutable unless there's a very good reason to make them mutable....If a class cannot be made immutable, limit its mutability as much as possible."

Examples of built in immutable classes in Java?
Following are some of the immutable classes in Java.
a.   String,
b.   Boolean,
c.    Byte,
d.   Short,
e.   Integer,
f.     Long,
g.   Float,
h.   Double etc.,

Benefits of Immutable class
a.   Immutable objects are inherently threading safe, so you no need to worry about thread safety.
b.   Since immutable objects state can't change, they never get into an inconsistent state.
c.    Since the properties (fields) of immutable objects can't change, you can cache the results of the operations performed on immutable objects and reuse the result.
d.   Immutable objects are best fit to use them as keys in HashMap.

How to create an Immutable object?
a.   Declare the class as final
b.   Make all of its fields as final and private
c.    Don’t expose any setter methods
d.   Any fields that reference to mutable objects such as Array, collections etc., don’t expose the data to outside the class (by returning the reference from function), and don't change the state of referenced data after construction of object.

a. Declare the class as final
A class that is declared final cannot be subclassed. If you don’t declare your class as final, then the sub class can change the immutable behavior to mutable. Let me explain with an example.

Suppose, in my organization, I want to increment salaries of my employees 10% on every year, no matter whether they perform well or not.
public class Increment {
  private final int inc;

  Increment(int inc) {
    this.inc = inc;
  }

  public int getInc() {
    return inc;
  }

}

public class MyIncrement extends Increment {
  private int inc;

  public int getInc() {
    return inc;
  }

  public void setInc(int inc) {
    this.inc = inc;
  }

  MyIncrement(int inc) {
    super(inc);
    this.inc = inc;
  }

}

public class Employee {
  private String id;
  private double salary;

  Employee(String id, double salary) {
    this.id = id;
    this.salary = salary;
  }

  public String getId() {
    return id;
  }

  public void setId(String id) {
    this.id = id;
  }

  public double getSalary() {
    return salary;
  }

  public void setSalary(double salary) {
    this.salary = salary;
  }

  @Override
  public String toString() {
    StringBuilder builder = new StringBuilder();
    builder.append("Employee [id=").append(id).append(", salary=")
        .append(salary).append(']');
    return builder.toString();
  }

}

Since Increment class is not final, I can create sub classes to Increment class. Assume I had written processEmployee method, which takes list of employees and an Increment instance and return all employees with updated salaries.

public static List<Employee> processEmployee(List<Employee> emps, Increment increment)
As you see the signature of processEmployee method, it takes list of employees and an increment object; increments the employees by the percentage specified in increment object and return employees with updated salaries. One problem here is, an Increment instance can take any Increment object (or) sub classes of Increment class.  Instead of Increment instance, I can pass MyIncrement instance and change the default 10% increment behavior.
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

public class Test {

  public static List<Employee> processEmployee(List<Employee> emps,
      Increment increment) {
    if (Objects.isNull(emps) || Objects.isNull(increment))
      return emps;

    for (Employee emp : emps) {
      emp.setSalary(emp.getSalary() + emp.getSalary()
          * increment.getInc());
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        System.out.println(e.getMessage());
      }
    }

    return emps;
  }

  public static void printEmployees(List<Employee> emps) {
    if (Objects.isNull(emps))
      return;

    emps.stream().forEach(System.out::println);
  }

  public static void main(String[] args) throws InterruptedException {
    Employee emp1 = new Employee("1", 12345.67);
    Employee emp2 = new Employee("2", 12345.67);
    Employee emp3 = new Employee("3", 12345.67);
    Employee emp4 = new Employee("4", 12345.67);

    List<Employee> emps = Arrays.asList(emp1, emp2, emp3, emp4);
    MyIncrement increment = new MyIncrement(10);

    printEmployees(emps);

    Thread t1 = new Thread() {
      public void run() {
        processEmployee(emps, increment);
      }
    };

    t1.start();

    increment.setInc(20);
    Thread.sleep(1000);

    increment.setInc(30);
    Thread.sleep(1000);

    increment.setInc(40);
    Thread.sleep(1000);

    t1.join();

    printEmployees(emps);
  }
}


Sample Output

Employee [id=1, salary=12345.67]
Employee [id=2, salary=12345.67]
Employee [id=3, salary=12345.67]
Employee [id=4, salary=12345.67]
Employee [id=1, salary=259259.07]
Employee [id=2, salary=382715.76999999996]
Employee [id=3, salary=506172.47]
Employee [id=4, salary=506172.47]


One simple solution to solve above problem is to make the class as Final.

Do you mean, a class must be final to achieve immutability?
No, it is not necessary to make the class final. You can make the getter methods (some times not all getter methods, make final only those that matters to immutability) as final, so the sub class can’t override the getter methods of sub class.

public class Increment {
  private final int inc;

  Increment(int inc) {
    this.inc = inc;
  }

  public final int getInc() {
    return inc;
  }

}


b. Make all of its fields as final and private
Fields declared as final are initialized once and never change their value under normal circumstances (By using reflections, you can change the value of final fields). One advantage of making the field final is, to improve performance. Compilers are allowed to keep the value of a final field cached in a register and not reload it from memory in situations where a non-final field would have to be reloaded. Keeping the field final emphasizes the fact that it cannot be changed anywhere else. 

c. Don’t expose any setter methods
Setter methods are used to change the state of an object. An immutable object doesn’t change its state after its creation. Don't provide setter methods to change the state.

d. Any fields that reference to mutable objects such as Array, collections etc., don’t expose the data to outside the class.
What happen if you expose an mutable object like List to outside. When you make an object as final, it makes the reference pointing to that object final, not the object. So when you expose a list kind of object, others can use it to change the state.

import java.util.List;

public final class Student {
  private final String id;
  private final String name;
  private final List<String> hobbies;

  Student(String id, String name, List<String> hobbies) {
    this.id = id;
    this.name = name;
    this.hobbies = hobbies;
  }

  public String getId() {
    return id;
  }

  public String getName() {
    return name;
  }

  public List<String> getHobbies() {
    return hobbies;
  }

}

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public class Test {

  public static void printList(List<String> hobbies) {
    if (Objects.isNull(hobbies))
      return;

    hobbies.stream().forEach(System.out::println);
  }

  public static void main(String[] args) throws InterruptedException {
    String id = "E553";
    String name = "Kiran Kumar";

    List<String> hobbies = new ArrayList<>();
    hobbies.add("Climbing Hills");
    hobbies.add("Painting");
    hobbies.add("Watching movies");

    Student s1 = new Student(id, name, hobbies);

    System.out.println("Hobbies of student s1 are");
    System.out.println("************************");
    printList(s1.getHobbies());

    /* Adding two more hobbies to s1 */
    s1.getHobbies().add("Playing Cricket");
    s1.getHobbies().add("Roaming");

    System.out.println("\nHobbies of student s1 are");
    System.out.println("************************");
    printList(s1.getHobbies());

  }
}


Output

Hobbies of student s1 are
************************
Climbing Hills
Painting
Watching movies

Hobbies of student s1 are
************************
Climbing Hills
Painting
Watching movies
Playing Cricket
Roaming


As you observe Student.java, even though I made the List<String> hobbies ad final, I can able to add new hobbies to the student s1 after object construction, which is violating immutability property. So don’t expose the mutable objects directly.

How can I expose mutable objects?
One simple way is, return the copy of mutable object.

public List<String> getHobbies() {
         return new ArrayList<> (hobbies);
}

and one more thing you have to do in the Student constructor. Constructor may be provided a mutable List. That way a Student's hobbies can be modified after object construction. So Change the definition of hobbies like below.

this.hobbies = new ArrayList<>(hobbies);
                    (or)
this.hobbies =Collections.unmodifiableList(hobbies);   
  

import java.util.ArrayList;
import java.util.List;

public final class Student {
  private final String id;
  private final String name;
  private final List<String> hobbies;

  Student(String id, String name, List<String> hobbies) {
    this.id = id;
    this.name = name;
    this.hobbies = new ArrayList<>(hobbies);
  }

  public String getId() {
    return id;
  }

  public String getName() {
    return name;
  }

  public List<String> getHobbies() {
    return new ArrayList<>(hobbies);
  }

}


Re run Test.java, you will get following output.

Output

Hobbies of student s1 are
************************
Climbing Hills
Painting
Watching movies

Hobbies of student s1 are
************************
Climbing Hills
Painting
Watching movies


I hope with above examples and description, you got some idea about immutable object. Follow all the rules that I specified in How to create an Immutable object? section. In real world applications, it is easy to break the rules and render the object unsafe. So you need to check whether an object is really immutable (or) not, by writing a function (or) by using some third party library. You can use MutabilityDetector library to check whether an object is immutable (or) not. MutabilityDetector library takes your class and verify whether it satisfying all the rules of immutability (or) not.


You may like




                                                                        Home

No comments:

Post a Comment