Friday, October 30, 2015

User defined Immutable Class

 
An immutable class is one whose state cannot be changed once created. There are certain guidelines to create a class immutable.
In this post, we will revisit these guidelines.
Sections:
 
Benefits of making a class immutable
Guidelines to make a class immutable
Benefits of making a class immutable
Lets first identify benefits of making a class immutable. Immutable classes are
1.      are simple to construct, test, and use
2.      are automatically thread-safe and have no synchronization issues
3.      do not need a copy constructor
4.      do not need an implementation of clone
5.      allow hashCode to use lazy initialization, and to cache its return value
6.      do not need to be copied defensively when used as a field
7.      make good Map keys and Set elements (these objects must not change state while in the collection)
8.      have their class invariant established once upon construction, and it never needs to be checked again
9.      always have “failure atomicity” (a term used by Joshua Bloch) : if an immutable object throws an exception, it’s never left in an undesirable or indeterminate state
Guidelines to make a class immutable
Java documentation itself has some guidelines identified in this link. We will understand what they mean actually:
1) Don’t provide “setter” methods — methods that modify fields or objects referred to by fields.
This principle says that for all mutable properties in your class, do not provide setter methods. Setter methods are meant to change the state of object and this is what we want to prevent here.
2) Make all fields final and private
This is another way to increase immutability. Fields declared private will not be accessible outside the class and making them final will ensure the even accidentally you can not change them.
3) Don’t allow subclasses to override methods
The simplest way to do this is to declare the class as final. Final classes in java can not be overridden.
4) Special attention when having mutable instance variables
Always remember that your instance variables will be either mutable or immutable. Identify them and return new objects with copied content for all mutable objects. Immutable variables can be returned safely without extra effort.
A more sophisticated approach is to make the constructor private and construct instances in factory methods.
Lets add all above rules and make something concrete class implementation.
import java.util.Date;
 
/**
* Always remember that your instance variables will be either mutable or immutable.
* Identify them and return new objects with copied content for all mutable objects.
* Immutable variables can be returned safely without extra effort.
* */
public final class ImmutableClass
{
 
    /**
    * Integer class is immutable as it does not provide any setter to change its content
    * */
    private final Integer immutableField1;
    /**
    * String class is immutable as it also does not provide setter to change its content
    * */
    private final String immutableField2;
    /**
    * Date class is mutable as it provide setters to change various date/time parts
    * */
    private final Date mutableField;
 
    //Default private constructor will ensure no unplanned construction of class
    private ImmutableClass(Integer fld1, String fld2, Date date)
    {
        this.immutableField1 = fld1;
        this.immutableField2 = fld2;
        this.mutableField = new Date(date.getTime());
    }
 
    //Factory method to store object creation logic in single place
    public static ImmutableClass createNewInstance(Integer fld1, String fld2, Date date)
    {
        return new ImmutableClass(fld1, fld2, date);
    }
 
    //Provide no setter methods
 
    /**
    * Integer class is immutable so we can return the instance variable as it is
    * */
    public Integer getImmutableField1() {
        return immutableField1;
    }
 
    /**
    * String class is also immutable so we can return the instance variable as it is
    * */
    public String getImmutableField2() {
        return immutableField2;
    }
 
    /**
    * Date class is mutable so we need a little care here.
    * We should not return the reference of original instance variable.
    * Instead a new Date object, with content copied to it, should be returned.
    * */
    public Date getMutableField() {
        return new Date(mutableField.getTime());
    }
 
    @Override
    public String toString() {
        return immutableField1 +" - "+ immutableField2 +" - "+ mutableField;
    }
}
 
Now its time to test our class:
class TestMain
{
    public static void main(String[] args)
    {
        ImmutableClass im = ImmutableClass.createNewInstance(100,"test", new Date());
        System.out.println(im);
        tryModification(im.getImmutableField1(),im.getImmutableField2(),im.getMutableField());
        System.out.println(im);
    }
 
    private static void tryModification(Integer immutableField1, String immutableField2, Date mutableField)
    {
        immutableField1 = 10000;
        immutableField2 = "test changed";
        mutableField.setDate(10);
    }
}
 
Output: (content is unchanged)
 
100 - test - Tue Oct 30 21:34:08 IST 2012
100 - test - Tue Oct 30 21:34:08 IST 2012
 
 
 
As it can be seen that even changing the instance variables using their references does not change their value, so the class is immutable.
 
 
 

Java Learning

1. Collections

2.  Reflection

3. Thread

4. Object oriented concepts

5. Eclipse debugging

6. RMI

7. Java Swing
 

hashCode() and equals() methos and it usage


HashCode and Equals method in Java object

Introduction

Java.lang.Object has methods called hasCode() and equals(). These methods play a significant role in the real time application. However its use is not always common to all applications. In some case these methods are overridden to perform certain purpose. In this article I will explain you some concept of these methods and why it becomes necessary to override these methods.

hashCode()

As you know this method provides the hashcode of an object. Basically the default implementation of hashCode() provided by Object is derived by mapping the memory address to an integer value. If look into the source of Object class , you will find the following code for the hashCode. public native int hashCode(); It indicates that hashCode is the native implementation which provides the memory address to a certain extent. However it is possible to override the hashCode method in your implementation class.

equals()

This particular method is used to make equal comparison between two objects. There are two types of comparisons in Java. One is using “= =” operator and another is “equals()”. I hope that you know the difference between this two. More specifically the “.equals()” refers to equivalence relations. So in broad sense you say that two objects are equivalent they satisfy the “equals()” condition. If you look into the source code of Object class you will find the following code for the equals() method.

 
public boolean equals(Object obj)
{
      return (this == obj);
}

 

Now I will explain you when to override the equals() and hashCode() methods and why it is necessary to override these methods. In this regard there is a rule of thumb that if you are going to override the one of the methods( ie equals() and hashCode() ) , you have to override the both otherwise it is a violation of contract made for equals() and hashCode(). Please refer to the Sun’s java docs for the method’s contract. I provide some test case scenario where you will find the significance of these methods. Case-1: You can override the hashCode method in your own way. Please refer to the following example.

package com.ddlab.core;
 
/**
 * @author Debadatta Mishra(PIKU)
 *
 */
public class Emp
{
      private int age ;
     
      public Emp( int age )
      {
            super();
            this.age = age;
      }
     
      public int hashCode()
      {
            return age;
      }
}

In the above example class “Emp” the variable age is the significant factor. Here the hashCode value will return the age of the person. Now let us consider the following test harness class.

 
package com.ddlab.core;
 
/**
 * @author Debadatta Mishra(PIKU)
 *
 */
public class TestEmp
{
      public static void main(String[] args)
      {
            Emp emp1 = new Emp(23);
            System.out.println("emp1.hashCode()--->>>"+emp1.hashCode());
      }
}

If you run the above program, the output will be the age what you have given i.e. 23. Now question arises whether there is any way we can get the original hashCode(). We can say that if we do not override the hashCode() method what could have been the hashCode of this object. However please do not feel depressed, Java provide another approach even if you have overridden the hashCode() method , still you can get the original hashCode of a particular class. Now run the following test harness program.

 
package com.ddlab.core;
 
package com.ddlab.core;
 
/**
 * @author Debadatta Mishra(PIKU)
 *
 */
public class TestEmp
{
      public static void main(String[] args)
      {
            Emp emp1 = new Emp(23);
            System.out.println("Overridden hashCode()--->>>"+emp1.hashCode());
            int originalHashCode = System.identityHashCode(emp1);
            System.out.println("Original hashCode of Emp---->>>"+originalHashCode);
      }
}

Here the output will be like this Overridden hashCode()--->>>23 Original hashCode of Emp---->>>8567361 As you know the above number is arbitrary, it depends upon your system. So then why it is necessary to override this method. There is one reason that if want to compare two objects based upon the equals() method. Although in a very simple class like “Emp”, you can achieve without overriding hashCode() method. But if you do this , you are going to violate the contract for the methods hashCode() and hashCode() of the object class. The similar case is for the method equals(). So funcational point is that if want to compare two objects based upon the equals() method you have to override both hashCode() and equals() methods. Please have look into the Emp class with the overridden methods and the related test harness class.

 
package com.ddlab.core;
 
/**
 * @author Debadatta Mishra(PIKU)
 *
 */
public class Emp
{
      private int age ;
     
      public Emp( int age )
      {
            super();
            this.age = age;
      }
     
      public int hashCode()
      {
            return age;
      }
     
      public boolean equals( Object obj )
      {
            boolean flag = false;
            Emp emp = ( Emp )obj;
            if( emp.age == age )
                  flag = true;
            return flag;
      }
}

The related test harness class is given below.

 
package com.ddlab.core;
 
/**
 * @author Debadatta Mishra(PIKU)
 *
 */
public class TestEmp
{
      public static void main(String[] args)
      {
            Emp emp1 = new Emp(23);
            Emp emp2 = new Emp(23);
            System.out.println("emp1.equals(emp2)--->>>"+emp1.equals(emp2));
      }
}

Case- 2 Think of a test scenario where you want to store your objects in a HasSet and you want to find a particular object. First let us see if we do not override the methods and we want to store the objects in the HashSet. Let us analyse the impact of it from the following code.

 
package com.ddlab.core;
 
/**
 * @author Debadatta Mishra(PIKU)
 *
 */
public class Emp
{
      private int age ;
     
      public Emp( int age )
      {
            super();
            this.age = age;
      }
     
}

In the above code it is a normal class. Now let us see the test harness class.

 
package com.ddlab.core;
 
import java.util.HashSet;
 
/**
 * @author Debadatta Mishra(PIKU)
 *
 */
public class TestEmp
{
      public static void main(String[] args)
      {
            Emp emp1 = new Emp(23);
            Emp emp2 = new Emp(24);
            Emp emp3 = new Emp(25);
            Emp emp4 = new Emp(26);
            Emp emp5 = new Emp(27);
            HashSet<Emp> hs = new HashSet<Emp>();
            hs.add(emp1);
            hs.add(emp2);
            hs.add(emp3);
            hs.add(emp4);
            hs.add(emp5);
           
            System.out.println("HashSet Size--->>>"+hs.size());
            System.out.println("hs.contains( new Emp(25))--->>>"+hs.contains(new Emp(25)));
            System.out.println("hs.remove( new Emp(24)--->>>"+hs.remove( new Emp(24));
            System.out.println("Now HashSet Size--->>>"+hs.size());
      }
}

If you run the above program, the will output will be like the following. HashSet Size--->>>5 hs.contains( new Emp(25))--->>>false hs.remove( new Emp(24)--->>>false Now HashSet Size--->>>5 It means that you can not find the object. However it is not the case for Integer object. You can put object of type Integer in a HashSet and you can try and you can see the effect. Now let us modify the “Emp” class so that we will get over the problems what we faced in the above test harness class.

 
package com.ddlab.core;
 
/**
 * @author Debadatta Mishra(PIKU)
 *
 */
public class Emp
{
      private int age ;
     
      public Emp( int age )
      {
            super();
            this.age = age;
      }
     
      public int hashCode()
      {
            return age;
      }
     
      public boolean equals( Object obj )
      {
            boolean flag = false;
            Emp emp = ( Emp )obj;
            if( emp.age == age )
                  flag = true;
            return flag;
      }
}

Bottom of Form

Here in the above class, we have overridden the hashCode() and equals() methods. Now if you run the same test harness class, you will get the desired output like the following. HashSet Size--->>>5 hs.contains( new Emp(25))--->>>true hs.remove( new Emp(24))--->>>true Now HashSet Size--->>>4 Case – 3 In this case you want to use your object as key not the value in the HashMap. So you have to override both the methods hashCode() and equals(). However it is left to the reader to create the object and test the feature in a Map. Case-4 If want to make your own immutable object , it will be wiser to override the equals() and hashCode() methods. To test the above programs, please create the appropriate package as mentioned in the program. You can also create your own package and modify the package name in the above programs. You can all the code in your favorable java editor.