Sunday, 8 July 2012

Shallow cloning vs Deep cloning in java

Cloning


Cloning means creating a copy of the object( not just the object reference copying as mentioned above). Cloning creates and returns a copy of the object, with the same class and with all the fields having the same values.

Car c1=new Car();
Car c2=(Car)c1.clone();

Here

  • c1!=c2 returns true,means c1 and c2 are referring to two different memory locations i.e. two different different objects.
  • c1.getClass()==c2.getClass() returns true means the cloned object and the original object should be of the same type.
  • c1.equals(c2) returns true means the cloned object data should be equal to the original one(Note: it can be changed any time after cloning).


Java supports two type of cloning: - Deep and shallow cloning.


By default shallow copy is used in Java. Object class has a method clone() which does shallow cloning.Shallow clone only copies the top level structure of the object not the lower levels.It is an exact bit copy of all the attributes.If the original object contains an object reference field, shallow cloning won't copy the object referred by that reference variable instead it just copy the reference i.e. the memory address.Hence both the original and cloned objects will share the same member object.

We need to override the Object.clone() method with extra logic to support the cloning of the member objects.This mechanism is called deep cloning.

Let us discuss deeply about shallow cloning and deep cloning.

Shallow cloning


One method of copying an object is the shallow copy.Let me to explain this concept with an example:

class Fan {
 String brand;
 int price;

 public Fan(String brand, int price) {
  this.brand = brand;
  this.price = price;
 }
}
class Room {
 int roomNo;
 String roomName;
 Fan fan;
 public Room(int roomNo, String roomName, Fan fan) {
  this.roomNo = roomNo;
  this.roomName = roomName;
  this.fan = fan;
 }
}
Shallow clone only copies the top level structure of the object not the lower levels.It is an exact bit copy of all the attributes.

The shallow copy is done for roomObj and new object roomObj_cloned is created but contained object(i.e.fan) of roomObj is not copied. The original object roomObj and cloned object roomObj_clone are sharing the same fan object. See the diagram given below.


If your class contains primitive attributes or immutable objects like String,Wrapper classes then shallow cloning makes a copy of primitive data types and immutable objects for the cloned object.In the above example, the roomObj contains primitive 'roomNo' field and immutable String object 'roomName'.You can see in the above diagram that shallow cloning creates a separate copy of these fields for the cloned object roomObj_cloned.Hence if the cloned object makes any change to the content of these variables, then it won't affect the original object and vice versa.

if the object being cloned contains a reference variable called obRef, then when the clone is made, obRef in the clone will refer to the same object as does obRef in the original. If the clone makes a change to the contents of the object referred to by obRef, then it will be changed for the original object, too.You can see in the above diagram that objects roomObj and roomObj_cloned are sharing the same Fan object.

Shallow Cloning Implementation


Object class provides a clone method and provides support for the shallow copy.The clone( ) method generates a duplicate copy of the object on which it is called. Only classes that implement the Cloneable interface can be cloned.The Cloneable interface defines no members. It is used to indicate that a class allows a bitwise copy of an object (that is, a clone) to be made. If you try to call clone( ) on a class that does not implement Cloneable, a CloneNotSupportedException is thrown. When a clone is made, the constructor for the object being cloned is not called. A clone is simply an exact copy of the original.

Object class clone method.

protected Object clone()
                throws CloneNotSupportedException

It returns ‘Object’ as type and you need to explicitly cast back to your original object.However, overriding clone() to return the appropriate type is preferable and eliminates the need for casting in the client (using covariant return types, since J2SE 5.0).
Cloning is a potentially dangerous action, because it can cause unintended side effects. For example, if the object being cloned contains a reference variable called obRef, then when the clone is made, obRef in the clone will refer to the same object as does obRef in the original. If the clone makes a change to the contents of the object referred to by obRef, then it will be changed for the original object, too. Here is another example. If an object opens an I/O stream and is then cloned, two objects will be capable of operating on the same stream. Further, if one of these objects closes the stream, the other object might still attempt to write to it, causing an error.

Because cloning can cause problems, clone( ) is declared as protected inside Object. This means that it must either be called from within a method defined by the class that implements Cloneable, or it must be explicitly overridden by that class so that it is public.The overridden public clone method should provide its own meaning for copy or to the least it should invoke the super.clone().When you invoke the super.clone() then you are dependent on the Object class’s implementation and what you get is a shallow copy.

Shallow cloning Example


In this example we are cloning a Room object which contains primitive field roomNo, immutable String object roomName and mutable Fan object.

package clone;

class ShallowCloning {

 /**
  * @param args
  * */
 public static void main(String[] args) {

  Fan fan = new Fan("USHA", 2000);
  Room roomObj = new Room(100, "Rose", fan);
  Room roomObj_cloned = (Room) roomObj.clone();
  System.out.println("Original object:roomObj=" + roomObj);
  System.out.println("Cloned object:roomObj_cloned=" + roomObj_cloned);

  /**
   * Modifying the cloned object field values. For simplicity I am not
   * using getters and setters for retrieving and modifying values.
   **/
  roomObj_cloned.roomName = "Lotus";
  roomObj_cloned.roomNo = 101;
  roomObj_cloned.fan.brand = "BAJAJ";
  roomObj_cloned.fan.price = 3000;

  System.out.println("After modifying the cloned object");

  System.out.println("Original object:roomObj=" + roomObj);
  System.out.println("Cloned object:roomObj_cloned=" + roomObj_cloned);
 }

}

class Fan {
 String brand;
 int price;

 public Fan(String brand, int price) {
  this.brand = brand;
  this.price = price;
 }

 public String toString() {
  return "Brand=" + brand + " Price=" + price;
 }
}

// Room class implementing mandatory Cloneable interface.

class Room implements Cloneable {
 int roomNo; // primitive type.
 String roomName; // immutable object.
 Fan fan; // mutable object.

 public Room(int roomNo, String roomName, Fan fan) {
  this.roomNo = roomNo;
  this.roomName = roomName;
  this.fan = fan;
 }

 /**
  * Room class overridding Object class clone method with public visibility.
  * You can override the clone method to return the Room type using covariant
  * return types, since J2SE 5.0
  **/

 public Object clone() {
  try {
   /**
    * Calling Object class clone method.That means performing shallow
    * cloning.
    **/
   return super.clone();

  } catch (CloneNotSupportedException e) {
   // throws when class not implements Cloneable interface.
   e.printStackTrace();
   return null;
  }
 }

 // Overridden to print the object state.
 public String toString() {
  return "RoomNo=" + roomNo + " RoomName=" + roomName + " Fan " + fan;
 }
}


The output will be

Original object:roomObj=RoomNo=100 RoomName=Rose Fan Brand=USHA Price=2000
Cloned object:roomObj_cloned=RoomNo=100 RoomName=Rose Fan Brand=USHA Price=2000
After modifying the cloned object
Original object:roomObj=RoomNo=100 RoomName=Rose Fan Brand=BAJAJ Price=3000
Cloned object:roomObj_cloned=RoomNo=101 RoomName=Lotus Fan Brand=BAJAJ Price=3000

Notice that changes made to the content of fields RoonNo and RoonName of cloned object doesn't affect the original object.But changes made to the Fan object of cloned object affects the original object.
As we discussed above shallow cloning creating a separate copy of primitive attributes(here roomNo) and immutable objects(here roomName) for the cloned object.In our example both the original object and the cloned object has its own 'roomNo' and 'roomName' fields.Hence if the cloned object makes any change to the content of these variables, then it won't affect the original object and vice versa.

If the object being cloned contains a reference variable called obRef, then when the clone is made, obRef in the clone will refer to the same object as does obRef in the original.Hence in our example both  the original object roomObj and the cloned object roomObj_cloned are sharing the same Fan object.if the cloned object makes any change to the Fan object , then it will affect the original object and vice versa.

Deep Cloning


In deep copy the object is copied along with the objects it refers to, which is not done by the default cloning mechanism.Let me to explain this mechanism with above mentioned Room class.The deep copy is done for roomObj and new object roomObj_cloned is created.See the diagram given below.


Can you see any difference in this diagram when comparing with the shallow cloning diagram?.Only difference is that original object roomObj and the cloned object roomObj_cloned has its own Fan object(NOTE: In the shallow cloning both of them were sharing the same Fan object).So any changes made to Fan object in roomObj_cloned will not reflect in the original object roomObj and vice versa.

Deep Cloning Implementation


We need to override the clone() method for the classes having non-primitive type members to achieve Deep Copy as Deep Copy requires the member objects to be cloned as well, which is not done by the default cloning mechanism.For Deep Copy, we need to ensure that the member classes also implement the Cloneable interface otherwise calling the clone() method on the objects of those classes will result into CloneNotSupportedException. So, to implement Deep Copy, we first need to ensure that all the member classes (at all the levels - like if the member class itself has a member of some class type then that class as well... and so on) are implementing the Cloneable interface. After that we override the clone() method in all those classes (even in the classes where we have only primitive type members otherwise we would not be able to call the protected clone()method of Object class on the instances of those classes inside some other class) and finally calling clone() method on the object members in the overriden clone() method definition.

Deep cloning Example

In this example we are cloning a Room object which contains primitive field roomNo, immutable String object roomName and mutable Fan object. In order to do deep copy of Room class, we will also have to implement Cloneable interface in Fan class and override clone method in Fan class too. If Fan class contains references of other objects, then you will have to implement Cloneable interface and override clone method in each sub class too.

package clone;

class DeepCloning {

 /**
  * @param args
  * */
 public static void main(String[] args) {

  Fan fan = new Fan("USHA", 2000);
  Room roomObj = new Room(100, "Rose", fan);
  Room roomObj_cloned = (Room) roomObj.clone();
  System.out.println("Original object:roomObj=" + roomObj);
  System.out.println("Cloned object:roomObj_cloned=" + roomObj_cloned);

  /**
   * Modifying the cloned object field values. For simplicity I am not
   * using getters and setters for retrieving and modifying values.
   **/
  roomObj_cloned.roomName = "Lotus";
  roomObj_cloned.roomNo = 101;
  roomObj_cloned.fan.brand = "BAJAJ";
  roomObj_cloned.fan.price = 3000;

  System.out.println("After modifying the cloned object");

  System.out.println("Original object:roomObj=" + roomObj);
  System.out.println("Cloned object:roomObj_cloned=" + roomObj_cloned);
 }

}

// Fan class implementing mandatory Cloneable interface.

class Fan implements Cloneable {
 String brand;
 int price;

 public Fan(String brand, int price) {
  this.brand = brand;
  this.price = price;
 }

 public Object clone() {
  try {
   /**
    * Calling Object class clone method.That means performing shallow
    * cloning.
    **/
   return super.clone();

  } catch (CloneNotSupportedException e) {
   // throws when class not implements Cloneable interface.
   e.printStackTrace();
   return null;
  }
 }

 public String toString() {
  return "Brand=" + brand + " Price=" + price;
 }
}

// Room class implementing mandatory Cloneable interface.

class Room implements Cloneable {
 int roomNo; // primitive type.
 String roomName; // immutable object.
 Fan fan; // mutable object.

 public Room(int roomNo, String roomName, Fan fan) {
  this.roomNo = roomNo;
  this.roomName = roomName;
  this.fan = fan;
 }

 /**
  * Room class overridding Object class clone method with public visibility.
  * You can override the clone method to return the Room type using covariant
  * return types, since J2SE 5.0
  **/

 public Object clone() {
  try {
   /**
    * Calling Object class clone method.That means performing shallow
    * cloning.
    **/
   Room clonedRoom = (Room) super.clone();
   /**
    * Explicitly calling the clone method of the member object fan to
    * create a copy of it.
    **/
   if (clonedRoom.fan != null) {
    clonedRoom.fan = (Fan) clonedRoom.fan.clone();
   }
   return clonedRoom;

  } catch (CloneNotSupportedException e) {
   // throws when class not implements Cloneable interface.
   e.printStackTrace();
   return null;
  }
 }

 // Overridden to print the object state.
 public String toString() {
  return "RoomNo=" + roomNo + " RoomName=" + roomName + " Fan " + fan;
 }
}
The output will be

Original object:roomObj=RoomNo=100 RoomName=Rose Fan Brand=USHA Price=2000
Cloned object:roomObj_cloned=RoomNo=100 RoomName=Rose Fan Brand=USHA Price=2000
After modifying the cloned object
Original object:roomObj=RoomNo=100 RoomName=Rose Fan Brand=USHA Price=2000
Cloned object:roomObj_cloned=RoomNo=101 RoomName=Lotus Fan Brand=BAJAJ Price=3000


Notice that changes made to the content of fan object of cloned object does't affect the original object( behavior was different in shallow cloning).

Deep cloning issues


When you need a deep copy then you need to implement it yourself. When the copied object contains some other object its references are copied recursively in deep copy. When you implement deep copy be careful as you might fall for cyclic dependencies. If you don’t want to implement deep copy yourselves then you can go for serialization. It does implements deep copy implicitly and gracefully handling cyclic dependencies.

Another problem arises when you try deep copying of a complex object. You're assuming that the clone() method of all member object variables also does deep copy; this is too risky of an assumption. You must control the code in all classes, or you must know that all classes involved in deep copy operation do such a copy in the right way.

No comments:

Post a Comment