Thursday 9 February 2017

How does clone method work

java.lang.Object provides default implementation of clone() method in Java. It's declared as protected and native in Object class, so i... thumbnail 1 summary
java.lang.Object provides default implementation of clone() method in Java. It's declared as protected and native in Object class, so implemented in native code. Since its convention to return clone() of an object by calling super.clone() method, any cloning process eventually reaches to java.lang.Object clone() method. This method, first checks if the corresponding object implements Cloneable interface, which is a marker interface. If that instance doesn't implement Cloneable then it throws CloneNotSupportedException in Java, a checked exception, which is always required to be handled while cloning an object.
In java, if a class needs to support cloning it has to do following things:

A) You must implement Cloneable interface.
B) You must override clone() method from Object class. [Its weird. clone() method should have been in Cloneable interface.]

Java docs about clone() method are given below (formatted and extract).
/*
Creates and returns a copy of this object. The precise meaning of  "copy" may depend on the class of the object.
The general intent is that, for any object x, the expression:
1) x.clone() != x will be true //guarantees that cloned object will have separate memory address assignment.
2) x.clone().getClass() == x.getClass() will be true, but these are not absolute requirements.//original and cloned objects should have same class type, but it is not mandatory.
3) x.clone().equals(x) will be true, this is not an absolute requirement.//original and cloned objects should have be equal using equals() method, but it is not mandatory.
*/

Lets see an Example :
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
Room.java
public class Room {
 private String roomSize;
 public Room(String roomSize){
  this.roomSize = roomSize;
}
 //Any Getters-Setters goes here
}
Flat.java
public class Flat implements Cloneable {
 private String flatNumber;
 private Room room;
 public Flat(String size,Room room){
   this.size = size;
   this.room = room;
 }
 public Object clone() {
  try {
   return (Flat)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) {
   Room room = new Room("40 X 40");
   Flat flat1 = new Flat(403 , room);
   Flat flat2 = (Flat)flat1.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 Flat using clone(), the field's flatNumber is getting copied with their respective values, but room is copied by reference — bit by bit, the memory address is getting copied.

Any changes you make to room 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 Room class as well implement the "Cloneable" interface and clone() method, then call the clone() method of the Room object inside the clone() method of the Flat object.

New Implementation
Room.java
public class Room {
    private String roomSize;
    public Room(String roomSize){
       this.roomSize = roomSize;
   }
    public Object clone() {
       try {
         return (Room)super.clone();
     }
      catch (CloneNotSupportedException e) {
       System.out.println("CloneNotSupportedException comes out : "
    +e.getMessage());
     }
   }
   //Any Getters-Setters goes here
}
Flat.java
public class Flat implements Cloneable {
    private String flatNumber;
 private Room room;
 public Flat(String size,Room room){
   this.size = size;
   this.room = room;
 }
    public Object clone() {
      Flat flat = null;
      try {
         flat = (Flat)super.clone();
     }
      catch (CloneNotSupportedException e) {
          System.out.println("CloneNotSupportedException comes out : "
    +e.getMessage());
     }
     flat.room = (Room) room.clone();
         return flat;
   }
    //Any Getters-Setters goes here
}
Main.java
public class Main {
    public static void main(String[] args) {
       Room room = new Room("40 X 40");
       Flat flat1 = new Flat(403, room);
       Flat flat2 = (Flat)flat1.clone();
   }
}

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

No comments

Post a Comment