Thursday, February 3, 2011

Cloning in Java

What is cloning?
Cloning as the word states make a copy of the object in Java.By default clone() method is provided by Object class sitting at the top of hierarchy.However subclass has to implement Cloneable marker interface for cloning to happen and override clone() method,otherwise we will get CloneNotSupportedException

Types of cloning?
There are two types of cloning-Shallow and Deep cloning.
Shallow Cloning is the default behavior when we call super.clone().
It creates the new object but copies the reference of the members inside the class.So for primitives and non mutable objects it works well.However for mutable objects both actual and cloned copy points to same reference.So any change made by one will be reflected for other also.


Deep Cloning removes the reference issue for the mutuable object.However developer has to actually iterate the tree and create a new object with the value of the cloned object and assign it explicitly so that cloned and actual object point to different objects in heap with same value.


To understand it better lets see this example of shallow cloning
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
package com.kunaal.clone;

import java.util.Calendar;
import java.util.GregorianCalendar;

/**
 * This example illustrates the shallow cloning behavior
 * Here when we cloned the object and changed non mutable variables like
 * Address and salary ,cloned and actual object had different values
 * 
 * However when we changed mutable variable dobCal both cloned and actual 
 * object reflected the modified value as both share the same reference.
 * 
 * @author Kunaal A Trehan
 *
 */
public class CloneExample {

 /**
  * @param args
  * @throws CloneNotSupportedException 
  */
 public static void main(String[] args) throws CloneNotSupportedException {
  PersonalDetails emp=createInstance();  
  PersonalDetails clonedEmp=(PersonalDetails)emp.clone();
  
  System.out.println("Original hashCode-" + emp.toString()  + "     Cloned hashCode-"+clonedEmp.toString());
  System.out.println("===========================================");
  modifyNonMutable(emp);
  System.out.println("After modifying non mutuable variables");
  System.out.println("Address is:" +emp.getAddress() +
    " , Salary is :" + emp.getSalary() );
  System.out.println("Cloned Address is:" +clonedEmp.getAddress() +
       " ,Cloned Salary is :" + clonedEmp.getSalary() );
  System.out.println("===========================================");
  modifyMutable(emp);
  Calendar clonedCal = clonedEmp.getDobCal();
  Calendar actCal = emp.getDobCal();
  System.out.println("After modifying mutuable variables");
  System.out.println("Cloned DOB is:" +clonedCal.get(Calendar.MONTH) +
               "-" + clonedCal.get(Calendar.DAY_OF_MONTH)+ 
               "-"+ clonedCal.get(Calendar.YEAR));
  System.out.println("Actual DOB is:" +actCal.get(Calendar.MONTH) +
             "-" + actCal.get(Calendar.DAY_OF_MONTH)+ 
             "-"+ actCal.get(Calendar.YEAR));
 }
 
 /**
  * Utility method for creating personal details object
  * @return
  */
 private static PersonalDetails createInstance(){
  PersonalDetails personalDetails=new PersonalDetails();
  personalDetails.setAddress("USA");
  personalDetails.setName("Jack");
  personalDetails.setPhoneNum("11100");
  personalDetails.setSalary(new Double(1000));
  personalDetails.setDobCal(getCal());
  
  return personalDetails;
 }
 
 /**
  * Utilty method which modifies non mutuable objects in Personal details  * 
  * @param personalDtls
  */
 private static void modifyNonMutable(PersonalDetails personalDtls){
  personalDtls.setAddress("UK");
  personalDtls.setSalary(new Double(122));
 }
 
 /**
  * Utility method modifying mutable objects in Personal details 
  * @param personalDtls
  */
 private static void modifyMutable(PersonalDetails personalDtls){
  personalDtls.resetDobCal(18, 2, 1978);
 }
 
 private static Calendar getCal(){
  Calendar cal =new GregorianCalendar();
  cal.set(Calendar.DAY_OF_MONTH, 1);
  cal.set(Calendar.MONTH, 1);
  cal.set(Calendar.YEAR, 2001);
  
  return cal;
 }
}

1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
/**
 * 
 */
package com.kunaal.clone;

import java.util.Calendar;

/**
 * Pojo class for personal details containing name,address,phone 
 * as attributes
 * 
 * @author Kunaal A Trehan
 *
 */
public class PersonalDetails implements Cloneable{

 private String name;
 
 private String address;
 
 private String phoneNum;
 
 private Double salary;
  
    private Calendar dobCal;

 /**
  * @return the name
  */
 public String getName() {
  return name;
 }

 /**
  * @param name the name to set
  */
 public void setName(String name) {
  this.name = name;
 }

 /**
  * @return the address
  */
 public String getAddress() {
  return address;
 }

 /**
  * @param address the address to set
  */
 public void setAddress(String address) {
  this.address = address;
 }

 /**
  * @return the phoneNum
  */
 public String getPhoneNum() {
  return phoneNum;
 }

 /**
  * @param phoneNum the phoneNum to set
  */
 public void setPhoneNum(String phoneNum) {
  this.phoneNum = phoneNum;
 }
 
 public Object clone() throws CloneNotSupportedException{
  return super.clone();
 }

 /**
  * @return the salary
  */
 public Double getSalary() {
  return salary;
 }

 /**
  * @param salary the salary to set
  */
 public void setSalary(Double salary) {
  this.salary = salary;
 }

 /**
  * @return the dobCal
  */
 public Calendar getDobCal() {
  return dobCal;
 }

 /**
  * @param dobCal the dobCal to set
  */
 public void setDobCal(Calendar dobCal) {
  this.dobCal = dobCal;
 }
 
 
 public void resetDobCal(int dd,int mon,int year){
  Calendar cal = getDobCal();
  cal.set(Calendar.DAY_OF_MONTH, dd);
  cal.set(Calendar.MONTH, mon);
  cal.set(Calendar.YEAR, year);
 }
}


To understand it better lets see this example of deep cloning

1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
/**
 * 
 */
package com.kunaal.clone;

/**
 * This example illustrates the deep cloning behavior
 * 
 * @author Kunaal A Trehan
 */
public class DeepCloneExample {

 /**
  * @param args
  * @throws CloneNotSupportedException 
  */
 public static void main(String[] args) throws CloneNotSupportedException {
  DeepCloneExample baseObject=new DeepCloneExample();
  DeepCloneExample.EmpInfo empInfo=baseObject.new EmpInfo(new StringBuffer("1"));
  DeepCloneExample.EmpInfo clone = (EmpInfo) empInfo.clone();
  
  System.out.println("Actual Object-"+ empInfo.toString() + "  ,Cloned Object-"+ clone.toString());
  //Modify the mutable object value
  empInfo.getInfo().append("2");
    
  System.out.println("Actual object info-" + empInfo.getInfo()+ 
    "        ,Cloned object info-" + clone.getInfo());
  
 }
 
 private class EmpInfo implements Cloneable{
  EmpInfo(StringBuffer data){
   this.info=data;
  }
  
  private StringBuffer info;

  /**
   * @return the info
   */
  public StringBuffer getInfo() {
   return info;
  }

  /**
   * @param info the info to set
   */
  public void setInfo(StringBuffer info) {
   this.info = info;
  }
  
  /**
   * Overridden clone method where we creating new object of 
   * mutuable string buffer variable and copying the
   * contents over there before assigning it to cloned object
   * This way cloned and actual object has different references
   * to mutable variable and both the objects are independent of each other 
   * changes.
   */
  public Object clone() throws CloneNotSupportedException{
   EmpInfo clone = (EmpInfo)super.clone();
   StringBuffer cloneInfo=new StringBuffer();
   cloneInfo.append(this.getInfo());
   
   clone.setInfo(cloneInfo);
   return clone;
  }
  
 }

}


Original hashCode-com.kunaal.clone.PersonalDetails@42e816     Cloned hashCode-com.kunaal.clone.PersonalDetails@9304b1
===========================================
After modifying non mutuable variables
Address is:UK , Salary is :122.0
Cloned Address is:USA ,Cloned Salary is :1000.0
===========================================
After modifying mutuable variables
Cloned DOB is:2-18-1978
Actual DOB is:2-18-1978



Uses of cloning?
So why we need cloning.Cloning acts as an alternative to serialization which is an expensive operation.
Moreover in highly concurrent scenarios where you have to perform some operation on the data set before any other change happens .You take the copy of existing data set ,clone it and use it.This enables you to have a copy of data set which wont be changed incase of any other event changes the dataset.
Incase you are iterating the collection and you are afraid that some one else can change the collection and you get ConcurrentModification exception.You can use the  clone() method to get a copy which is owned by you only.


Hope it helps,comments are welcome

No comments:

Post a Comment