Which is better option: Java clone or Copy constructors or Copy static factory?

Java

Which is better option: Java clone or Copy constructors or Copy static factory?

The Cloneable interface was intended for objects to advertise that they permit cloning. Unfortunately it fails to serve this purpose. It’s primary flaw is that it lacks a clone method, and Object’s clone method is protected. So what does Cloneable do, given that it contains no methods? It determines the behavior of Object’s protected clone implementation: if a class implements Cloneable, Object’s clone method returns a field-by-field copy of the object; otherwise it throws CloneNotSupportedException. Cloning creates an object without calling a constructor.

Cloning an object, means creating a similar copy of an object which basically should conform to the following rules:

1) x.clone() != x
2) x.clone().getClass() == x.getClass()
3) x.clone().equals(x)

Here is the method signature of clone method in Object class:

protected Object clone() throws CloneNotSupportedException;

So as you notice the protected modifier, it is not possible for us to call clone() method directly on any object. We have to override this method as a public method and provide implementation for it in our class in order to access it. If no specific implementation is needed, we can just return super.clone(). As covariant returns are possible after Java 5, we can modify the return value of clone to return the object of our class(never make the client do anything the library can do for the client). So if we are writing our employee class, here is how the clone() method would like:

@Override
public Employee clone() throws CloneNotSupportedException {
    return (Employee) super.clone();
}

But note that the clone method in Object class checks if our class implements Cloneable interface. If it does not implement it, then it throws CloneNotSupportedException. Otherwise it creates a new copy. But note that clone method never calls constructor to create copy of an object. So if you want to keep track of number of instances getting created for a class by incrementing a static counter inside the constructor, this would not work as constructor never gets called. The clone method instead does field-by-field copy of instance properties from the object memory and returns it to caller. So it is must for a class to implement the marker interface, Cloneable, if it has to provide an option for cloning it without getting CloneNotSupportedException. But notice that the code which calls clone() should handle this exception. Otherwise it would result in compiler error. Yes, it’s a pain point and is criticized for this.

Please note that when we clone an object, the constructor does not get called. It would rather make a field-by-field copy of all the member variables present in the address location of the original object. And now when there are object references, the reference gets copied but not the original object. Hence both the original and cloned objects point to the same member object. So changes made in one object would automatically be visible to the other. So how to resolve this problem?

The easiest solution is to implement clone method for the member objects too.

Now consider another case when member field is declared final, this will make the situation even worse. As the field is declared final, we can not assign a new value to it in clone method as it is declared final. So how to deal with this?

Here is the solution: Use a copy constructor or copy
factory and return the new instance from the clone.

A copy constructor is simply a constructor that takes a single argument whose type is the class containing the constructor, for example,

public Yum(Yum yum);

A copy factory is the static factory analog of a copy constructor:

public static Yum newInstance(Yum yum);

So, as of now copy constructor or copy factory is the best way for copying objects as it resolves many problems with flawed clone method:

1. None of the classes have to implement the marker interface Cloneable
2. As clone is not needed, there is no need of catching CloneNotSupportedException
3. As clone is not needed, there is no need of typecasting the object on calling super.clone()

But here comes the problem, say suppose you have a subclass for member object.

And the reason is obvious. The copy constructor of your object did not know about this new subclass created. We can actually modify the object constructor to include instanceOf checks for member object, but this is not the right way of doing things. Rather it’s better if we revert back to our earlier solution where we used copy constructor in case of final fields and use clone method for the classes which have Inheritance hierarchy.

Implementing clone method in the right way is very complicated. So it is better to stay away from clones as much as possible. It is better to go with copy constructors as long as the composed objects does not have any inheritance hierarchy.

About Anoop Kumar Rai