Monday, 26 September 2016

Explore Java Cloning !!

In Java, whenever we assign an object reference to another, it is just a bit by bit copy of the memory address, so any changes made using first object reference will be reflected in others and vice versa.
For example:
MyClass mc = new MyClass();
MyClass mc_1 = mc; 
In the above snippet, any changes made to using mc object reference will be seen by mc_1, and the reverse is also true. What if you need an object reference, which is a copy of another object, but you want that both objects should not interfere their state, i.e. they independently modify their instance variables, which should not be 0seen in other object references. In short, they should have different memory addresses, but the same state when getting copied. Afterward, they can modify their state as needed. This is where cloning comes into the picture.
Cloning is a mechanism that provides a way to create a copy of an object and the term copy is specific to the implementation of the class.
So,cloning will firmly say:

MyClass mc = new MyClass();
i) mc.clone() != mc -true always
ii) mc.getClass() == mc.clone().getClass() -true always
iii) mc.clone().equals(mc) -true just after clone() gets called, after that,
it may be false
Significance of the above snippet:
  • mc.clone() != mc means memory address of both object reference should not be same.
  • mc.getClass()  == mc.clone().getClass() means both should be an object reference of the same class.
  • mc.clone().equals(mc) means the moment clone() method get called both should have same state (data), but afterwards they may have different state (data).

To implement the cloning in our class, we need to implement the Cloneable interface. The Cloneable interface is a Marker interface, which has no method. So, whenever we implement the Cloneable interface, being developers, it's our responsibility to write the definition of the clone() method.
So, what if our class is implementing the Cloneable interface but not providing the definition of the clone() method, so nothing will happen — cloning will not be achieved. The purpose of the Cloneable interface is to tell the JVM that this class is eligible for cloning, but clone() will achieve the actual cloning.  
If your class will not implement the Cloneable interface, as below, and you run it, it will throw the java.lang.CloneNotSupportedException, which is a checked exception.
public class MyClone {
    int x;
    public static void main(String[] args) throws CloneNotSupportedException {
        MyClone c = new MyClone();
        MyClone a = (MyClone) c.clone();              // Type-cast is required
    }
}
Because clone() is a part of the Object class, and Object won't implement the Cloneable interface, when our own class will not implement the Cloneable interface, the JVM will not know that this class is eligible for cloning, so CloneNotSupportedException comes out.
Only two thing are possible when we say MyClone a = (MyClone) c.clone();:
  • Either it will return the cloned object.
  • Or it will throw CloneNotSupportedException.
As it is clear that it is not mandatory to implement clone() in your class when you want to clone the object, if you don't, the clone() method in Object is declared protected — only subclasses and members of the same package will be able to invoke clone() on the object. If you want to change that, you should override it and make it public.
Checks before the clone() method call:
if(c instanceof Cloneable) {
    MyClone a = (MyClone) c.clone();
}
Note: No constructor gets called when clone() gets called. It's our responsibility to properly set all the member variables of that class.
Implementation
BathTub.java
public class BathTub {
 private String tubName;
 public BathTub(String tubName){
  this.tubName = tubName;
}
 //Any Getters-Setters goes here
}
BathRoom.java
public class BathRoom implements Cloneable {
 private int legth;
 private int width;
 private int height;
 private BathTub bathTub;
 public BathRoom(int legth, int width, int height, BathTub bathTub){
   this.legth = legth;
   this.width = width;
   this.height = height;
   this.bathTub = bathTub;
 }
 public Object clone() {
  try {
   return (BathRoom)super.clone();
 }
  catch (CloneNotSupportedException e) {
   System.out.println("CloneNotSupportedException comes out : "+e.getMessage());
  }
 }
//Any Getters-Setters goes here
}

Main.java
public class Main {
  public static void main(String[] args) {
   BathTub bathTub = new BathTub("BreezeTub");
   BathRoom bathRoom_1 = new BathRoom(10,10,12, bathTub);
   BathRoom bathRoom_2 = (BathRoom)bathRoom_1.clone();
  }
}
Here super.clone() is getting called inside clone(). As we know, clone() is declared in Object, so it is inherited by every Java object. Calling super.clone() copies our superclass' fields and makes bitwise copies of the fields. This known as shallow copy, which means when you copy BathRoom using clone(), the field's length, width, and height are getting copied with their respective values, but bathTub is copied by reference — bit by bit, the memory address is getting copied.
Any changes you make to bathTub of the original object will be reflected in the cloned object and vice versa. To solve this, we need deep copy. Now, we need to change the BathTub class as well implement the "Cloneable" interface and clone() method, then call the clone() method of the BathTub object inside the clone() method of the BathRoom object.
New Implementation
BathTub.java
public class BathTub {
    private String tubName;
    public BathTub(String tubName){
       this.tubName = tubName;
   }
    public Object clone() {
       try {
         return (BathTub)super.clone();
     }
      catch (CloneNotSupportedException e) {
       System.out.println("CloneNotSupportedException comes out : "+e.getMessage());
     }
   }
   //Any Getters-Setters goes here
}
BathRoom.java
public class BathRoom implements Cloneable {
    private int legth;
    private int width;
    private int height;
    private BathTub bathTub;
    public BathRoom(int legth, int width, int height, BathTub bathTub){
       this.legth = legth;
       this.width = width;
       this.height = height;
       this.bathTub = bathTub;
   }
    public Object clone() {
      BathRoom bathRoom = null;
      try {
         bathRoom = (BathRoom)super.clone();
     }
      catch (CloneNotSupportedException e) {
          System.out.println("CloneNotSupportedException comes out : "+e.getMessage());
     }
     bathRoom.bathTub = (BathTub) bathTub.clone();
         return bathRoom;
   }
    //Any Getters-Setters goes here
}
Main.java
public class Main {
    public static void main(String[] args) {
       BathTub bathTub = new BathTub("BreezeTub");
       BathRoom bathRoom_1 = new BathRoom(10,10,12, bathTub);
       BathRoom bathRoom_2 = (BathRoom)bathRoom_1.clone();
   }
}

I hope that this will give a better understanding of cloning and its implementation. Happy learning!


No comments:

Post a Comment

Attend Online Java Certification Training and excel your career

Hello Java Developer,  Are you staying at home and worried about your future? Do not waste the time in worrying. International certifi...