Thursday, May 29, 2014

immutability in Java

An object is considered immutable if its state cannot change after it is constructed.

Immutable objects are particularly useful in concurrent applications. Since they cannot change state, they cannot be corrupted by thread interference or observed in an inconsistent state.

Strategy for Defining Immutable Objects

The following rules define a simple strategy for creating immutable objects.
  1. Don't provide "setter" methods — methods that modify fields or objects referred to by fields.
  2. Make all fields final and private.
  3. Set all instance data in the constructor.
  4. Don't allow sub-classes to override methods. The simplest way to do this is to declare the class as final. A more sophisticated approach is to make the constructor private and construct instances in factory methods.
  5. If the instance fields include references to mutable objects, don't allow those objects to be changed:
    • Don't provide methods that modify the mutable objects.
    • Clone mutable objects for which a reference to them is returned.
    • Clone mutable objects for which a reference to them is received.
    • Implement a deep clone if the default shallow clone is not correct for a properly behaved immutable object.
 Lets add all above rules and make something concrete class implementation.
By making class final:

This is the most simple way of making a class mutable.
public final class FinalPersonClass {       

      private final String name;
      private final int age;      

      public FinalPersonClass(final String name, final int age) { 

            super();
            this.name = name;
            this.age = age; 
      } 

      public int getAge() { 

            return age;
      } 

      public String getName() { 

            return name;
      }
} 

 Using Factory methods and making constructor private:

//No need to make class final here 
public class FinalPerson { 
          
     private final String name;
      private final int age;       

      private FinalPerson(final String name, final int age) { 

            super();
            this.name = name;
            this.age = age;
      } 

      public int getAge() { 

            return age;
      } 

      public String getName() { 

            return name; 
      } 

      public FinalPerson getFinalPerson(final String name, final int age) {
           return new FinalPerson(final String name, final int age);
     }
} 


Making a class immutable in Java, which includes mutable member variable.

When an immutable class is implemented, mutable objects passed to or returned from an immutable object must be properly cloned. Consider the following class declarations: a DiskDriveInfo class and a User class. The DiskDriveInfo is intended to be immutable. The User encapsulates which user has shared access to the disk drive. The User object with shared access is stored as part of the DiskDriveInfo object. In the following example, the designer of the class was careful to make the class final and all fields private, and to provide only getter methods. Is the DiskDriveInfo class immutable? If not, what needs to be done to make it so?

class User
{
  private String userName;
  private String userID;
  private int userNode;

  User(String name, int node)
  {
    userName = name;
    userNode = node;
  }
  public void setUserName(String name)
  {
    userName = name;
  }
  public void setUserID(String userid)
  {
    userID = userid;
  }
  public void setUserNode(int node)
  {
    userNode = node;
  }
  public String userName()
  {
    return userName;
  }
}


final class DiskDriveInfo
{
  private int driveSize;
  private String volumeLabel;
  private User driveShare;

  DiskDriveInfo(int size, String volLabel, User share)
  {
    driveSize = size;
    volumeLabel = volLabel;
    driveShare = share;
  }
  public int size()
  {
    return driveSize;
  }
  public String label()
  {
    return volumeLabel;
  }
  public User share()
  {
    return driveShare;
  }
}

The DiskDriveInfo class is not immutable. Objects of this class can be changed. Consider the following code that creates a DiskDriveInfo object and tests its immutability:

class Test
{
  private static final int sizeInMeg = 200;
  public static void main(String args[])
  {
    User share1 = new User("Duke", 10);                       //1
    DiskDriveInfo dd = new DiskDriveInfo(sizeInMeg, "myDrive",
                                         share1);             //2
    User share = dd.share();
    System.out.println("User with shared access is " +
                       share.userName());

    share1.setUserName("Fred");                               //3
    System.out.println("User with shared access is " +
                       share.userName());
  }
}

If we run the program we will get output like this:

Output
================================================================================
User with shared access is Duke
User with shared access is Fred
What went wrong? This code creates a User object, share1, at //1, with the user name Duke. A supposedly immutable DiskDriveInfo object is created at //2 and is passed a reference to the User object. The DiskDriveInfo object is queried, and the shared owner, Duke, is printed. The User object, share1, changes its name to Fred at //3. When the DiskDriveInfo object is queried again for the user name, it discovers that the name changed from Duke to Fred.
The problem is that the DiskDriveInfo constructor receives a reference to the User object and does not make a copy, or clone, of this object. Therefore, the DiskDriveInfo constructor receives a copy of the reference to the User object. Now the DiskDriveInfo object's driveShare field and the local variable, share1, in main of class Test, reference the same object. Therefore, any changes made through either reference affect the same object. Figure shows the object layout after the code at //1 is executed.




http://ptgmedia.pearsoncmg.com/images/art_haggar2_praxis64/elementLinks/haggar2_fig1.gif
 
After the code at //2 is executed, the object layout looks as shown in Figure

http://ptgmedia.pearsoncmg.com/images/art_haggar2_praxis64/elementLinks/haggar2_fig2.gif
Notice that because the reference to the User object is not cloned, both the share1 and driveShare references share the same User object. After the code at //3 is executed, the object layout as shown in Figure.

http://ptgmedia.pearsoncmg.com/images/art_haggar2_praxis64/elementLinks/haggar2_fig3.gif

To correct this problem, the DiskDriveInfo class must clone any mutable object to which it receives a reference. It then has a reference to its own copy of the object that cannot be changed by other code.
The modified DiskDriveInfo class that supports cloning looks like this:


final class DiskDriveInfo
{
  //As before...
  DiskDriveInfo(int size, String volLabel, User share)
  {
    driveSize = size;
    volumeLabel = volLabel;
    driveShare = (User)share.clone();
  }
  public User share()
  {
    return (User)driveShare.clone();
  }
}

 Because you are cloning the User object, its definition must change as well.


class User implements Cloneable
{
  //As before...
  public Object clone()
  {
    try {
      return super.clone();
    }
    catch (CloneNotSupportedException e) {
      //This should not happen, since this class is Cloneable.
      throw new InternalError();
    }
  }
}

 With these changes to the User object, running the previous test code produces the correct output:
Output
================================================================================
User with shared access is Duke
User with shared access is Fred
Because the User object is cloned on the constructor call, the code that subsequently changes the User object at //1 has no effect on the DiskDriveInfo object. The implementation of the immutable DiskDriveInfo class is now correct. The object layout looks as shown in Figure

http://ptgmedia.pearsoncmg.com/images/art_haggar2_praxis64/elementLinks/haggar2_fig4.gif

Achieving Immutability with Builder Design Pattern:

In most of the classes in our real applications there are many fields. Also, most of these fields are not mandatory for object creation. For example, a user in a real application will have a username, password, firstname, lastname, creationDate, emailAddress, etc., but for user creation here, only a username and password are required. 

The Builder Pattern separates the construction of a complex object from its representation so that the same construction process can create different representations.
The builder design pattern provides a way for you to build complex immutable objects. The process is:
  1. The client calls a constructor (or static factory) with all the required parameters and gets a builder object.
  2. The client calls setter like methods to set each optional parameter of interest.
  3. Finally the client calls the build method to generate the object which is immutable.

import java.math.BigDecimal;

/**
* Immutable, hence thread safe CashBalance objec
*/

public final class CashBalance {
  
 private BigDecimal initialBalance, totCredits, totDebits;
  
  //construct
  public CashBalance(CashBalanceBuilder builder) {
  this.initialBalance = builder.initialBalance;
  this.totCredits = builder.totCredits;
  this.totDebits = builder.totDebits;
 }
    
 public static class CashBalanceBuilder {
   
  //has same fields as the object it is going to build
   protected BigDecimal initialBalance, totCredits, totDebits;
  //define the setters that return itself
   CashBalanceBuilder setInitialBalance(BigDecimal initialBalance) {
   this.initialBalance = initialBalance;
   return this;
  }
   CashBalanceBuilder setTotCredits(BigDecimal totCredits) {
    this.totCredits = totCredits;
   return this;
  }
  CashBalanceBuilder setTotDebits(BigDecimal totDebits) {
   this.totDebits = totDebits;
   return this;
  }
 }
  
 //only getter methods and no setter methods as it is an immutable object
}

The client code will look like this :


public static void main(String[] args) {
   CashBalance.CashBalanceBuilder builder = 
           new CashBalance.CashBalanceBuilder(
                    .setInitialBalance(BigDecimal.valueOf(250.00))
                    .setTotCredits(BigDecimal.valueOf(250.00))
                    .setTotDebits(BigDecimal.valueOf(250.00));
                    CashBalance bal = new CashBalance(builder);
}

How to use Date object in immutable class

This is a commonly asked scenario in interviews while discussing about immutable classes.
Some time you may need to write immutable class which includes mutable classes like java.util.Date, despite storing Date into final field it can be modified internally, if internal date is returned to the client. In order to preserve immutability in such cases, its advised to return copy of original object.
Consider the below code:

public final class ImmutableReminder{

    private final Date remindingDate;
  

    public ImmutableReminder (Date remindingDate)
    {
        if(remindingDate.getTime() < System.currentTimeMillis()){

            throw new IllegalArgumentException("Can not
                   set reminder” + “ for past time: " + remindingDate);
         }
         // creating new object of Date first
this.remindingDate = new Date(remindingDate.getTime());
    }

    public Date getRemindingDate() {
      //returning clone of the date object 
     return (Date) remindingDate.clone();
    }
} 


No comments :

Post a Comment