Monday, September 30, 2013

How clone method works in Java

clone is a tricky method from java.lang.Object class, which is used to create copy of an Object in Java. Intention of clone() method is simple, to provide a cloning mechanism, but some how it's implementation became tricky and has been widely criticized from long time. Anyway, we will not go to classic debate of clone in Java, at-least for now; instead, we will try to learn how clone method works in Java. To be fair, understating cloning mechanism in Java is not easy and even experienced Java programmer fail to explain how cloning of mutable object works, or difference between deep copy and shallow copy in Java. In this three part article, we will first see working of clone method in Java, and in second part we will learn how to override clone method in Java, and finally we will discuss deep copy vs shallow copy mechanism. The reason I chose to make this a three part article, is to keep focus on one thing at a time. Since clone() itself is confusing enough, it's best to understand concept one by one. In this post, we will learn what is clone method, what it does and How clone method works in Java. By the way, clone() is one of the few fundamental methods defined by objects, others being equals, hashcode(), toString() along with wait and notify methods.

What is clone of object in Java

An object which is returned by clone() method is known as clone of original instance. A clone object should follow basic characteristics e.g. a.clone() != a, which means original and clone are two separate object in Java heap, a.clone().getClass() == a.getClass() and clone.equals(a), which means clone is exact copy of original object. These characteristic is followed by a well behaved, correctly overridden clone() method in Java, but it's not enforced by cloning mechanism. Which means, an object returned by clone() method may violate any of these rules. By following convention of returning object by calling super.clone(), when overriding clone() method, you can ensure that it follows first two characteristics. In order to follow third characteristic, you must override equals method to enforce logical comparison, instead of physical comparison exists in java.lang.Object. For example, clone() method of Rectangle class in this method return object, which has these characteristics, but if you run same program by commenting equals(), you will see that third invariant i.e. clone.equals(a) will return false. By the way there are couple of good items on Effective Java regarding effective use of clone method, I highly recommend to read those items after going through this article.

How Clone method works in Java

How Clone method works in Javajava.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 it's convention to return clone() of object by calling super.clone() method, any cloning process eventually reaches to java.lang.Object clone() method. This method, first checks if corresponding object implements Cloneable interface, which is a marker interface. If that instance doesn't implements Cloneable then it throws CloneNotSupported in Java, a checked exception, which is always required to be handled while cloning an object. If object pass this check, than java.lang.Object's clone() method creates a shallow copy of object and returned it to the caller. Since Object class' clone() method creates copy by creating new instance, and then copying field-by-field, similar to assignment operator, it's fine for primitives and Immutable object, but not suited if your class contains some mutable data-structure e.g. Collection classes like ArrayList or arrays. In that case, both original object and copy of object will point to the same object in heap. You can prevent this by using technique known as deep cloning, on which each mutable field is cloned separately. In short, here is how clone method works in Java :

1) Any class calls clone() method on instance, which implements Cloneable and overrides protected clone() method from Object class, to create a copy.

  Rectangle rec = new Rectangle(30, 60);
  logger.info(rec);
      
    try {
         logger.info("Creating Copy of this object using Clone method");
         Rectangle copy = rec.clone();
         logger.info("Copy " + copy);
          
    } catch (CloneNotSupportedException ex) {
         logger.debug("Cloning is not supported for this object");
    }

2) Call to clone() method on Rectangle is delegated to super.clone(), which can be a custom super class or by default java.lang.Object

    @Override
    protected Rectangle clone() throws CloneNotSupportedException {
        return (Rectangle) super.clone();
    }

3) Eventually call reaches to java.lang.Object's clone() method, which verify if corresponding instance implements Cloneable interface, if not then it throws CloneNotSupportedException, otherwise it creates a field-by-field copy of instance of that class and returned to caller.

So in order for clone() method to work properly, two things need to happen, a Class should implement Cloneable interface and should override clone() method of Object class. By the way this was this was the simplest example of overriding clone method and how it works, things gets more complicated with real object, which contains mutable fields, arrays, collections, Immutable object and primitives, which we will see in second part of this Java Cloning tutorial series.

Java clone() method Example

In this article, we have not seen complexity of overriding clone method in Java, as our Rectangle class is very simple and only contains primitive fields, which means shallow cloning provided by Object's clone() method is enough. But, this example is important to understand process of Object cloning in Java, and How clone method works. Here is complete code of this clone() method overriding example :

import org.apache.log4j.Logger;

/**
  * Simple example of overriding clone() method in Java to understand How Cloning of
  * Object works in Java.
  *
  * @author
 */
public class JavaCloneTest {
    private static final Logger logger = Logger.getLogger(JavaCloneTest.class);
  
    public static void main(String args[]) {

        Rectangle rec = new Rectangle(30, 60);
        logger.info(rec);
      
        Rectangle copy = null;
        try {
            logger.info("Creating Copy of this object using Clone method");
            copy = rec.clone();
            logger.info("Copy " + copy);
          
        } catch (CloneNotSupportedException ex) {
            logger.debug("Cloning is not supported for this object");
        }
      
        //testing properties of object returned by clone method in Java
        logger.info("copy != rec : " + (copy != rec));
        logger.info("copy.getClass() == rec.getClass() : " + (copy.getClass() == rec.getClass()));
        logger.info("copy.equals(rec) : " + copy.equals(rec));
      
        //Updating fields in original object
        rec.setHeight(100);
        rec.setWidth(45);
      
        logger.info("Original object :" + rec);
        logger.info("Clonned object  :" + copy);
    }
 
}

public class Rectangle implements Cloneable{
    private int width;
    private int height;
  
    public Rectangle(int w, int h){
        width = w;
        height = h;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public void setWidth(int width) {
        this.width = width;
    }
  
    public int area(){
        return widthheight;
    }
  
    @Override
    public String toString(){
        return String.format("Rectangle [width: %d, height: %d, area: %d]", width, height, area());
    }

    @Override
    protected Rectangle clone() throws CloneNotSupportedException {
        return (Rectangle) super.clone();
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Rectangle other = (Rectangle) obj;
        if (this.width != other.width) {
            return false;
        }
        if (this.height != other.height) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 47  hash + this.width;
        hash = 47  hash + this.height;
        return hash;
    }
  
  
  
}

Output:
2013-05-20 23:46:58,882 0    [main] INFO  JavaCloneTest  - Rectangle [width: 30, height: 60, area: 1800]
2013-05-20 23:46:58,882 0    [main] INFO  JavaCloneTest  - Creating Copy of this object using Clone method
2013-05-20 23:46:58,882 0    [main] INFO  JavaCloneTest  - Copy Rectangle [width: 30, height: 60, area: 1800]

2013-05-20 23:46:58,882 0    [main] INFO  JavaCloneTest  - copy != rec : true
2013-05-20 23:46:58,882 0    [main] INFO  JavaCloneTest  - copy.getClass() == rec.getClass() : true
2013-05-20 23:46:58,882 0    [main] INFO  JavaCloneTest  - copy.equals(rec) : true

2013-05-20 23:46:58,882 0    [main] INFO  JavaCloneTest  - Original object :Rectangle [width: 45, height: 100, area: 4500]

2013-05-20 23:46:58,882 0    [main] INFO  JavaCloneTest  - Cloned object  :Rectangle [width: 30, height: 60, area: 1800]

From output, you can clearly see that cloned object has same attribute as original object in Java. Also changing attribute of original object is not affecting state of copy object, because they only contains primitive fields, had then contain any mutable object, it would have affected both of them. You can also see that it follow standard properties of cloned object i.e. clone != original, clone.getClass() == original.getClass() and clone.equals(original).

Things to Remember - Clone method in Java


1) Clone method is used to create a copy of object in Java. In order to use clone() method, class must implement java.lang.Cloneable interface and override protected clone() method from java.lang.Object. A call to clone() method will result in CloneNotSupportedException, if that class doesn't implement Cloneable interface.

2) No constructor is called during cloning of Object in Java.

3) Default implementation of clone() method in Java provides "shallow copy" of object, because it creates copy of Object by creating new instance and then copying content by assignment, which means if your Class contains a mutable field, then both original object and clone will refer to same internal object. This can be dangerous, because any change made on that mutable field will reflect in both original and copy object. In order to avoid this, override clone() method to provide deep copy of object.

4) By convention, clone of an instance should be obtained by calling super.clone() method, this will help to preserve invariant of object created by clone() method i.e. clone != original and clone.getClass() == original.getClass(). Though these are not absolute requirement as mentioned in Javadoc.

5) Shallow copy of an instance is find, until it only contains primitives and Immutable objects, otherwise, you need to modify one or more mutable fields of object returned by super.clone, before returning it to caller.

That's all on How clone method works in Java. Now we know, what is clone and what is Cloneable interface, couple of things about clone method and what does default implementation of clone method do in Java. This information is enough to move ahead and read second part of this Java cloning tutorial, on which we will learn, how to override clone() method in Java, for classes composed with primitives, Mutable and Immutable objects in Java.

Recommended Book
Like most of important topics in Java, Joshua Bloch has shared some words of wisdom on object cloning and clone method in Java. I highly suggest going through those items on his evergreen Effective Java book.

10 comments :

Anonymous said...

"a.clone() != a, which means original and clone are two separate object in Java heap, a.clone().getClass() == a.getClass() and clone.equals(a), which means clone is exact copy of original object"

So hashcode is different but equals should return true. Isn't this a violation of the equals-hashcode contract?

Anonymous said...

How do you relate hash code here... ?
c the hash code calculation in above rectangle class.. its has nothing to do with the reference of the object..
I have a question.. Why is clone method protected by implementation ?

Anonymous said...

Why it is protected - so that you can't call it on any object - the default shallow copy can be risky unless the class explicitly says its ok. Classes should override it, return the appropriate type (instead of Object) and make the visibility public.
http://stackoverflow.com/questions/1138769/why-is-the-clone-method-protected-in-java-lang-object

Sasi kumar said...

HI, even hashcode is different , but equals should return true, it is not violation of equal-hashcode contract, because if two objects having same hashcode, those objects value may be same , may not be same. but when two objects having same value, both objects hashcode must be same.

Anonymous said...

please provide the links for the 2nd and 3rd part of the clone article

Anonymous said...

Return type of OVERRIDDEN clone method is Object in superclass, how can it be narrower i.e. Rectangle in subclass? Please correct me if I am wrong.

Sajal Gupta said...

"Anonymous said...
Return type of OVERRIDDEN clone method is Object in superclass, how can it be narrower i.e. Rectangle in subclass? Please correct me if I am wrong."
--> Java overriding rule: 'The return type should be the same or a subtype of the return type declared in the original overridden method in the superclass'

Anonymous said...

Nice explanation.

Anonymous said...

this is nice explanation :)
where is 2nd and 3rd page?

VINOD said...

Does Cloning makes use of reflection? If yes could you please explain why and how?

Thanks.

Post a Comment