Tuesday, March 4, 2014

Cloning in Java

A clone is an exact copy of the original. In java, it essentially means the ability to create an object with similar state as the original object.

Sections in this post: 
  1. Default Java cloning
  2. Cloneable Interface
  3. Shallow cloning
  4. Deep cloning
    1. Deep cloning by overriding clone method
    2. Deep cloning using copy constructor
    3. Deep cloning using The factory Method
    4. Deep cloning by Serialzation
  5. Summery

Java cloning

 

By default, java cloning is ‘field by field copy’ i.e. as the Object class does not have idea about the structure of class on which clone() method will be invoked. So, JVM when called for cloning, do following things:

1) If the class has only primitive data type members then a completely new copy of the object will be created and the reference to the new object copy will be returned.

2) If the class contains members of any class type then only the object references to those members are copied and hence the member references in both the original object as well as the cloned object refer to the same object.

In java, if a class needs to support cloning it has to do following things:

A) It must implement Cloneable interface.
B) It must override clone() method from Object class.

Cloneable Interface
public interface Cloneable
A class implements the Cloneable interface to indicate to the Object.clone() method that it is legal for that method to make a field-for-field copy of instances of that class. Invoking Object's clone method on an instance that does not implement the Cloneable interface results in the exception CloneNotSupportedException being thrown.
By convention, classes that implement this interface should override Object.clone (which is protected) with a public method.

Java docs about clone() method says:

1. x.clone() != x will be true
2. x.clone().getClass() == x.getClass() will be true, 
                           but these are not absolute requirements.
3. x.clone().equals(x) will be true, this is not an absolute requirement 
  1.  First statement guarantees that cloned object will have separate memory address assignment.
  2. Second statement suggest that original and cloned objects should have same class type, but it is not mandatory.
  3. Third statement suggest that original and cloned objects should have be equal using equals() method, but it is not mandatory.
Cloning can be categorized in two types:

 

Shallow Cloning 

 

This is default implementation in java. In overridden clone method, if you are not cloning all the object types (not primitives), then you are making a shallow copy. Let's see one example:

Class Employee implements cloneable Interface and has Department as its member variable

public class Employee implements Cloneable{

   private int employeeId;
   private String employeeName;
   private Department department;

   public Employee(int id, String name, Department dept) {
     this.empoyeeId = id;
     this.employeeName = name;
     this.department = dept;
   }   
   
   public int getEmployeeId() {
     return empoyeeId;
   }

   public void setEmployeeId(int employeeId) {
     this.employeeId = empoyeeId;
   }

   public String getEmployeeName() {
     return employeeName;
  }

   public void setEmployeeName(String employeeName) {
     this.employeeName = employeeName;
  }

   public Department getDepartment() {
     return department;
   }

   public void setDepartment(Department department) {
     this.department = department;
   }

   @Override
   protected Object clone() throws CloneNotSupportedException {
     return super.clone();
 }
}
  Class Department:

public class Department
{
 private int id;
 private String name;

 public Department(int id, String name)
 {
  this.id = id;
  this.name = name;
 }

 public int getId() {
  return id;
 }

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

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }
}
  Test Class:

public class TestCloning {

    public static void main(String[] args)

               throws CloneNotSupportedException {

         Department dept = new Department(1, "Human Resource");
         Employee original = new Employee(1, "Admin", dept); 

        //Lets create a clone of original object

         Employee cloned = (Employee) original.clone();


        //Let verify using employee id, if cloning actually worked

          System.out.println(cloned.getEmpoyeeId());

        //Verify JDK's rules
    //Must be true and objects must have different memory addresses
         System.out.println(original != cloned);

    //As we are returning same class; so it should be true 
         System.out.println(original.getClass() == cloned.getClass());


     //Default equals method checks for references so it should be false.
        // If we want to make it true,
        //we need to override equals method in Employee class.
        System.out.println(original.equals(cloned));
    }
} 
 
Output:
========================================================================== 
 1
true
true
false
 What will happen if we don't implement cloneable Interface???

Check the following code for Class Employee, I have not Implemented Cloneable interface this time but clone method is still there. No compilation error will be shown but when we run the code run-time Exception will come.

public class Employee {

   private int empoyeeId;
   private String employeeName;
   private Department department;

   public Employee(int id, String name, Department dept) {
     this.empoyeeId = id;
     this.employeeName = name;
     this.department = dept;
   }   
   
   public int getEmpoyeeId() {
     return empoyeeId;
 }

 public void setEmpoyeeId(int empoyeeId) {
  this.empoyeeId = empoyeeId;
 }

 public String getEmployeeName() {
  return employeeName;
 }

 public void setEmployeeName(String employeeName) {
  this.employeeName = employeeName;
 }

 public Department getDepartment() {
  return department;
 }

 public void setDepartment(Department department) {
  this.department = department;
 }

 @Override
   protected Object clone() throws CloneNotSupportedException {
     return super.clone();
 }
}

Output
===========================================================================

Exception in thread "main" java.lang.CloneNotSupportedException:
 com.Employee
 at java.lang.Object.clone(Native Method)
 at com.Employee.clone(Employee.java:48)
 at com.TestCloning.main(TestCloning.java:13)
  We can see even if Object class has clone method still we need to implement Cloneable interface to use clone method.

 

Deep cloning


We want a clone which is independent of original and making changes in clone should not affect original.

But before seeing how deep cloning works, lets see what happens when we create two objects of employee class:

public class TestCloning {

 public static void main(String[] args) 
 
  Department hr = new Department(1, "Human Resource");
  Employee original = new Employee(1, "Admin", hr);
  Employee cloned = (Employee) original.clone();

  //Let change the department name in cloned object 
  //and we will verify in original object
  cloned.getDepartment().setName("Finance");

  System.out.println(original.getDepartment().getName());
 }
}

Output: 
========================================================================== 
Finance

cloned object changes are visible in original also. To prevent this from happening we need to do deep cloning.

Let see how it can be done

1. Deep Cloning by overriding clone() 
  Modified clone() method in Employee class

@Override
protected Object clone() throws CloneNotSupportedException {
 Employee cloned = (Employee)super.clone();
 cloned.setDepartment((Department)cloned.getDepartment().clone());
 return cloned;
}
 override clone method in Department class also:
 
@Override
protected Object clone() throws CloneNotSupportedException {
 return super.clone();
}
  Test class:

public class TestCloning {

 public static void main(String[] args) 
                        throws CloneNotSupportedException { 
 
    Department hr = new Department(1, "Human Resource");
    Employee original = new Employee(1, "Admin", hr);
    Employee cloned = (Employee) original.clone();

    //Let's change the department name in cloned object 
                //and we will verify in original object
    cloned.getDepartment().setName("Finance");

   System.out.println(original.getDepartment().getName());
 }
}
 
Output: 
========================================================================== 
Human Resource
So deep cloning requires satisfaction of following rules.
  • All the member classes in original class should support cloning and in clone method of original class in context should call super.clone() on all member classes.
  • If any member class does not support cloning then in clone method, one must create a new instance of that member class and copy all its attributes one by one to new member class object. This new member class object will be set in cloned object.

 

2. Deep Cloning Using Copy Constructors


A copy constructor is a constructor that takes only one argument which is of the type as the class in which the copy constructor is implemented. For example, let us assume a class namely Employee and it has a constructor called copy constructor which expects only one argument of type Employee.

Let’s see an example

public class Employee {

   private int empoyeeId;
   private String employeeName;
   private Department department;

      
   public int getEmpoyeeId() {
     return empoyeeId;
   }

   public void setEmpoyeeId(int empoyeeId) {
     this.empoyeeId = empoyeeId;
   }

   public String getEmployeeName() {
     return employeeName;
  }

   public void setEmployeeName(String employeeName) {
     this.employeeName = employeeName;
  }

   public Department getDepartment() {
     return department;
   }

   public void setDepartment(Department department) {
     this.department = department;
   }
   public Employee(int id, String name, Department department) {

        this.id = id;

        this.name = name;

        this.department = department;

    } 
 
   //copy constructor
public Employee(Employee oldEmployee) {

        this.id = oldEmployee.id;
        this.name = oldEmployee.name;
        this.department = oldEmployee.department;  //shallow cloning
    }
} 
  Class Department:

public class Department
{
 private int id;
 private String name;

 public Department(int id, String name)
 {
  this.id = id;
  this.name = name;
 }

 public int getId() {
  return id;
 }

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

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }
 public Department(int id, String name) {

      this.id = id;

      this.name = name;

 } 
 
  //copy constructor
 public Department(Department oldDepartment) {

      this.id = oldDepartment.id;
      this.name = oldDepartment.name;
  }
 }
  Test Class

public class CopyConstructorTest {
 
 public static void main(String[] args) {

        Department department = new Department(1, "Finance");
        Employee originalEmployee = new Employee(1, "Ram", department);

        Employee clonedEmployee = new Employee(originalEmployee);

        System.out.println("Original:- " + originalEmployee);
        System.out.println("Duplicate:- " + clonedEmployee);
        System.out.println();

        clonedEmployee.setId(2);
        clonedEmployee.setName("Laxman");
        clonedEmployee.getDepartment().setName("HR");

        System.out.println("Original:- " + originalEmployee);
        System.out.println("Duplicate:- " + clonedEmployee);
    }
}
Output
============================================================================
Original:- Employee Id: 1   Employee Name: Ram  Department Id: 1
Department Name: Finance
Duplicate:- Employee Id: 1  Employee Name: Ram  Department Id: 1
Department Name: Finance
Original:- Employee Id: 1   Employee Name: Ram  Department Id: 1
Department Name: HR
Duplicate:- Employee Id: 2  Employee Name: Laxman   Department Id: 1
Department Name: HR
What happens in the above example is, when we change the department name of clonedEmployee, the change comes up with the originalEmployee also. Why it behaves so because what we implement here is shallow cloning

Let’s have a small change in the copy constructor of Employee class to implement Deep cloning 

public Employee(Employee oldEmployee) {
  this.id = oldEmployee.id;
  this.name = oldEmployee.name;
 this.department = newDepartment(oldEmployee.department); //deep cloning
}
  Now we test again and the output is

Original:-  Employee Id: 1  Employee Name: Ram  Department Id: 1    Department Name: Finance
Duplicate:- Employee Id: 1  Employee Name: Ram  Department Id: 1    Department Name: Finance
Original:-  Employee Id: 1  Employee Name: Ram  Department Id: 1    Department Name: Finance
Duplicate:- Employee Id: 2  Employee Name: Laxman   Department Id: 1    Department Name: HR
 Now everything goes fine and this is what we call deep cloning using copy constructors.

But wait until Inheritance comes

let's have one class Manager which inherit Employee

Class Manager:

public class Manager extends Employee{        
private int managerId;
           
    public int getManagerId() {
        return managerId;
    }

    public void setManagerId(int managerId) {
        this.managerId = managerId;
    }

    public Manager (Employee obj, int ManagerId) {
        super(obj);
        this.managerId= ManagerId;
    }
   
    public Manager(Manager obj) {
        super(obj);
        this.managerId= obj.managerId;
    }
}

Test class:

public class TestCloning {        

   public static void main(String[] args) {

        Department department1 = new Department(1, "Finance");
        Department department2 = new Department(2, "It");       

        Employee employee1 = new Employee(1, "Ram", department1);
        Employee employee2 = new Employee(1, "Laxman", department2); 
 
        Manager manager1 = new Manager(employee2,1);
 
        Employee clone1 = new Employee(employee1);
        Employee clone2 = new Employee(manager1);       

        System.out.println("clone1:- " +clone1.toString());
        System.out.println(clone1.getClass());
        System.out.println("clone2:- " + clone2.toString());
        System.out.println(clone2.getClass());
    }
}
Output
===================================================================
clone1:- Employee Id: 1    Employee Name: Ram    Department Id: 1    Department Name: Finance
class com.Employee
clone2:- Employee Id: 1    Employee Name: Laxman    Department Id: 2    Department Name: It
class com.Employee
What just happened here? We passed Manger object to clone2 to but we got Employee class object after cloning.

3. Deep cloning by The Factory Method:

In certain situations we can’t make our copy constructor public, so it has to be private. Now we need to think about an equivalent and yes, fortunately we are able to find out another workaround for achieving the same result- The Factory Method. Let’s have a look.

Class Employee

public class Employee {
    
private String name;
    private int id;
    private Department department;

    public Department getDepartment() {
        return department;
    }
 
    public String getName() {
        return name;
    }

    public int getId() {
        return id;
    }

    public void setDepartment(Department department) {
        this.department = department;
    }

    public void setName(String name) {
        this.name = name;
    }

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

    public Employee(int id, String name, Department department) {
        this.id = id;
        this.name = name;
        this.department = department;
    }

    private Employee(Employee oldEmployee) {
        this.id = oldEmployee.id;
        this.name = oldEmployee.name;
        //deep cloning
        this.department = new Department(oldEmployee.department);
    }

    @Override
    public String toString() {
        return "Employee Id: " + id + "\tEmployee Name: " + name + "\t" 
       + department;
    }

    public static Employee copy(Employee originalEmployee) {
        return new Employee(originalEmployee);
    }
}
Test Class:

public class TestCloning {   
public static void main(String[] args) {

        Department department = new Department(1, "Finance");
        Employee originalEmployee = new Employee(1, "Ram", department);
        Employee clonedEmployee = Employee.copy(originalEmployee);

        System.out.println("Original:- " + originalEmployee);
        System.out.println("Duplicate:- " + clonedEmployee);
        System.out.println();

        clonedEmployee.setEmployeeId(2);
        clonedEmployee.setEmployeeName("Laxman");
        clonedEmployee.getDepartment().setName("HR");

        System.out.println("Original:- " + originalEmployee);
        System.out.println("Duplicate:- " + clonedEmployee);
    }  
}
Output
===================================================================
Original:- Employee Id: 1    Employee Name: Ram    Department Id: 1    Department Name: Finance
Duplicate:- Employee Id: 1    Employee Name: Ram    Department Id: 1    Department Name: Finance

Original:- Employee Id: 1    Employee Name: Ram    Department Id: 1    Department Name: Finance
Duplicate:- Employee Id: 2    Employee Name: Laxman    Department Id: 1    Department Name: HR

4. Deep cloning with serialization

Java serialization is convenient. Many classes are made serializable by simply declaring them to implement java.io.Serializable. Thus, a whole hierarchy of classes can be made cloneable by deriving them from a base Serializable class 

Lets see the code sample:

public Employee clone(Employee obj) {
   
 try
    {
        ByteArrayOutputStream out = new ByteArrayOutputStream ();
        ObjectOutputStream oout = new ObjectOutputStream (out);
        oout.writeObject (obj);
       
        ObjectInputStream in = new ObjectInputStream (
            new ByteArrayInputStream (out.toByteArray ())); 
 
            //This returned Employee object will be a cloned object
return (Employee)in.readObject ();
    }
    catch (Exception e)
    {
        throw new RuntimeException ("cannot clone class [" +
            obj.getClass ().getName () + "] via serialization: " +
            e.toString ());
    }
}
For more details about Serialization refer to serialization in Java.

 

The Summery

 

The following table recaps the properties of all cloning approaches from several perspectives: speed, resource utilization, class design constraints, object graph handling.

Object.clone()
Speed High
Resource utilization Low
Class design constraints Does not work with deep final fields; does not work with inner classes; must implement Cloneable; medium amount of manual class maintenance
Object graphs Does not handle object graphs transparently
Copy construction
Speed High
Resource utilization Low
Class design constraints Superclasses and subclasses must cooperate; copy constructor required; a lot of manual class maintenance
Object graphs Does not handle object graphs transparently
Serialization
Speed Low
Resource utilization High; creates redundant immutable fields
Class design constraints Must implement Serializable; first non-Serializable class needs an accessible no-arg constuctor
Object graphs Handles object graphs
Reflection
Speed Medium
Resource utilization Medium
Class design constraints Does not work with final fields; does not work with inner classes; each class must provide no-arg constructor
Object graphs Handles object graphs



No comments :

Post a Comment