Monday, 24 April 2017

Object cloning in java

In Java if you assign an object variable to another variable the reference is copied which means both variable will share the same reference. In this case any change in one object variable will be reflected in another.

As example – If there is a class Test and you create an object of that class and then assign that reference to another object variable.

Test obj1 = new Test();
Test obj2 = obj1;

Here both obj1 and obj2 will have the same reference.

Object cloning

What is the option then if you want to quickly create an object using the existing object in such a way that you get a new instance (reference is not shared) with the same content for the fields in the new object as in existing object.

That’s when you can use clone() method which creates an exact copy of the existing object. Then you can modify the cloned object without those modification reflecting in original object (Well we’ll go into shallow copy and deep copy a little later).

clone() method

clone() method is defined as protected in the Object class which you must override as public in any derived classes that you want to clone.

Signature of clone method in Object class

protected native Object clone() throws CloneNotSupportedException;

Process of cloning

There are two required steps if you want to clone any object.

  1. You need to call clone() method of the Object class or provide your own implementation by overriding the clone() method in your class.
  2. Your class, whose object you want to clone, must implement Cloneable interface which is part of java.lang package. Not implementing Cloneable interface will result in CloneNotSupportedException exception being thrown when clone method is called.

Here note that Cloneable interface is a marker interface and defines no members of its own.

Scenario 1- Calling clone() method from a method

Here we have a class Test which implements Cloneable interface and it has a method cloneIt() which calls the clone() method of the Object class.

Class Test

public class Test implements Cloneable{
  int a;
  float f;
 
 Test cloneIt(){
  Test test = null;
  try {
   // Calling clone() method of Object class
   test = (Test)super.clone();
  } catch (CloneNotSupportedException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  return test;
  
 }
}

CloningDemo class

public class CloningDemo {

 public static void main(String[] args) {
  Test t1 = new Test();
  t1.a = 10;
  t1.f = 13.4F;
  // Calling method to clone
  Test t2 = t1.cloneIt();
  System.out.println("t1.a " + t1.a + " t1.f " + t1.f);
  
  System.out.println("t2.a " + t2.a + "t2.f " + t2.f);

  if(t1 != t2){
   System.out.println("Different instances");
  }else{
   System.out.println("Same instances");
  }
 }

}

Output

t1.a 10 t1.f 13.4
t2.a 10t2.f 13.4
Different instances

Here you can see that Test class object t1 is cloned and a new instance t2 is created, in the code even reference equality is checked and you can see that the reference is not shared and both are indeed different instances.

Points to note

Some of the points to note from this code –

  1. Class whose object has to be cloned should implement Cloneable interface, otherwise java.lang.CloneNotSupportedException exception will be thrown.
  2. clone() method is a protected method in Object class, since all the classes inherit from Object class so you can call the protected method of the super class.
  3. While cloning bitwise copy of the object is created.

Scenario 2 – Overriding clone method

Another way to provide clone functionality is to override the clone() method in the class in that case it has to be a public method in order to be accessible.

If we change the classes used in the above example to have overridden clone method and calling that clone method then the ' structure will be as follows –

Test class

public class Test implements Cloneable{
 int a;
 float f;
 // Override clone method
 public Object clone(){
  Object obj = null;
  try {
   // Calling clone() method of Object class
   obj = super.clone();
   } catch(CloneNotSupportedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
  }
  return obj;
 }
}

CloningDemo class

public class CloningDemo {

 public static void main(String[] args) {
  Test t1 = new Test();
  t1.a = 10;
  t1.f = 13.4F;
  // Call clone method
  Test t2 = (Test)t1.clone();
  System.out.println("t1.a " + t1.a + " t1.f " + t1.f);
  
  System.out.println("t2.a " + t2.a + " t2.f " + t2.f);
  
  if(t1 != t2){
   System.out.println("Different instances");
  }else{
   System.out.println("Same instances");
  }
 }
}

Output

t1.a 10 t1.f 13.4
t2.a 10 t2.f 13.4
Different instances

Advantages of cloning

If you have an object, creation of which using the usual way is costly; as example if you have to call DB in order to get data to create and initialize your object. In that scenario rather than hitting DB every time to create your object you can cache it, clone it when object is needed and update it in DB only when needed.

Actually there is a design pattern called prototype design pattern which suggests the same approach.

Shallow copy

In the above examples only primitive types are used so there is no problem, if you change any primitive value that won’t reflect in other object.

What if there is another object reference in your class? As already mentioned when you clone an object all the values for the fields are copied to the cloned object. Since Java is pass by value, if the field value is a reference to an object (a memory address) it copies that reference to the field of the cloned object. In that case referenced field is shared between both objects and any change made to the referenced field will be reflected in the other object too.

This process of cloning when the field values are copied to the new object is known as shallow copy. Shallow copies are simple to implement and typically cheap, as they can be usually implemented by simply copying the bits exactly.

Example code

Let’s try to clarify it with an example. Here we have a Class called ClassA with 2 int fields. Another class ClassB which has a ClassA object and an int field.

Then you create an object objB of ClassB and then clone it to get a new object objB2. In both of these objects reference of ClassA object will be shared.

ClassA

public class ClassA {
 private int i;
 private int j;
 // Constructor
 public ClassA(int i, int j){
  this.i = i;
  this.j = j;
 }
 public void setI(int i) {
  this.i = i;
 }
 public void setJ(int j) {
  this.j = j;
 }
 public int getI() {
  return i;
 }
 public int getJ() {
  return j;
 }
}

ClassB

public class ClassB implements Cloneable{
 private int x;
 private ClassA objA;
 
 public ClassB(int x, ClassA objA){
  this.x = x;
  this.objA = objA;
 }
 public Object clone() throws CloneNotSupportedException{
  return super.clone();
 }
 public int getX() {
  return x;
 }
 public ClassA getObjA() {
  return objA;
 }
 public void setX(int x) {
  this.x = x;
 }
 public void setObjA(ClassA objA) {
  this.objA = objA;
 }
}

CloningDemo class

public class CloningDemo {

 public static void main(String[] args) {
  ClassB objB = new ClassB(10, new ClassA(20, 30));
  
  ClassB objB2 = null;
  try {
   objB2 = (ClassB)objB.clone();
   
  } catch (CloneNotSupportedException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  // value of field i of objA changed 
  // in cloned object
  objB2.getObjA().setI(100); 
  // Value of primitive field x changed 
  // in cloned object
  objB2.setX(1);
  System.out.println("objB.x- " +  objB.getX() + " objB.objA.i- " + objB.getObjA().getI() + " objB.objA.j- " + objB.getObjA().getJ());
  System.out.println("objB2.x- " +  objB2.getX() + " objB2.objA.i- " + objB2.getObjA().getI() + " objB2.objA.j- " + objB2.getObjA().getJ());

 }
}

Output

objB.x- 10 objB.objA.i- 100 objB.objA.j- 30
objB2.x- 1 objB2.objA.i- 100 objB2.objA.j- 30 

In CloningDemo class objB is cloned to get a new instance objB2. Value of primitive field x is changed in the cloned object objB2, you can see that both objects have their own independent values for field x.

Coming to object field, objB2 will have its own field objA where value of field i is changed. You can see that value in the original object objB for objA.i is also changed as the objA reference is shared between the objects. That’s one drawback of shallow copy.

Deep Copy

If you don’t want references of object being copied during the cloning process then option is deep copy. When a deep copy is done objects referenced by the cloned object are distinct from those referenced by original object, and independent.

Deep copies are more expensive, as you need to create additional objects, and can be substantially more complicated, due to references possibly forming a complicated graph.

Example Code

public class ClassB implements Cloneable{
 private int x;
 private ClassA objA;
 
 public ClassB(int x, ClassA objA){
  this.x = x;
  this.objA = objA;
 }
 public Object clone() throws CloneNotSupportedException{
  // Cloning object
  ClassB objB = (ClassB)super.clone();
  // Explicitly assigning reference to new ClassA object
  objB.setObjA(new ClassA(100, objA.getJ()));
  return objB;
 }
 public int getX() {
  return x;
 }
 public ClassA getObjA() {
  return objA;
 }
 public void setX(int x) {
  this.x = x;
 }
 public void setObjA(ClassA objA) {
  this.objA = objA;
 }
}

Here ClassB is changed to create a deep copy while cloning the object. Notice in the clone method, for the cloned object a new ClassA object is created with a new value(100) for field i and keeping the old value for field j. Since it’s a new object so reference is not shared with the classA object of the original object.

If we run this code now using the below program -

public class CloningDemo {

 public static void main(String[] args) {
  ClassB objB = new ClassB(10, new ClassA(20, 30));
  
  ClassB objB2 = null;
  try {
   objB2 = (ClassB)objB.clone();
   
  } catch (CloneNotSupportedException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  // value of field i of objA changed 
  // in cloned object
  //objB2.getObjA().setI(100); 
  // Value of primitive field x changed 
  // in cloned object
  objB2.setX(1);
  System.out.println("objB.x- " +  objB.getX() + " objB.objA.i- " + objB.getObjA().getI() 
     + " objB.objA.j- " + objB.getObjA().getJ());
  System.out.println("objB2.x- " +  objB2.getX() + " objB2.objA.i- " + objB2.getObjA().getI() 
    + " objB2.objA.j- " + objB2.getObjA().getJ());

 }

}

Output

objB.x- 10 objB.objA.i- 20 objB.objA.j- 30
objB2.x- 1 objB2.objA.i- 100 objB2.objA.j- 30

Now you can see that value for field i is changed only in cloned object not in the original object.

That's all for this topic Object cloning in Java. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. Serialization in Java
  2. serialVersionUID and versioning in Java Serialization
  3. Nested class and Inner class in Java
  4. Marker interface in Java

You may also like -

>>>Go to Java advance topics page


No comments:

Post a Comment