“Cloneable” mixin interface does not ensure that the implementing class is cloneable
“Cloneable” interface was intended as a way to advertise that the class is cloneable. But there is no guarantee that the class has implemented a proper version of the clone functionality for the reason that the “Cloneable” interface does not have any methods and does not force it’s implementor to do anything.
What does “Cloneable” do?
The base class Object” has a “clone()” method. When a class implements “Cloneable”, the class gets the clone functionality that is supported in the “Object” class.
If the class had not implemented the “Cloneable” interface, then the “clone()” method would have thrown a “CloneNotSupportedException”.
Basic implementation of clone()
The basic implementation of “clone()” would be to invoke “Object” class’ “clone()” method using “super.clone()” and then typecasting the returned object to the particular class. Also,the “CloneNotSupportedException” needs to be handled in the implementation.
@Override
public MyClass clone() {
try {
return (MyClass) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError(); // Can’t happen
}
CloneNotSupportedException should have been a UnCheckedException.Why?
Once the class implements the “Cloneable” interface, the class supports clone anyways. Even then, the client is being forced to write boilerplate code purely to handle the “CloneNotSupportedException”. This could have been avoided if “CloneNotSupportedException” was an unchecked exception.
Covariant return types
The class “Object” has a “clone()” method with “Object” as the return type.
public Object clone();
When “MyClass” overrides this method, it is allowed to return “MyClass”.
public MyClass clone();
This is referred to as covariant return types. Return types are covariant. An overriding method’s return type can be a subclass of the overridden methods’s return type.
Object’s Clone implementation works fine if the class only has primitives and immutable members
For the scenario where the class only has primitives, the clone() method would return a functioning clone.
Object’s clone implementation is NOT a good option when the class has mutable members
The reason is that when the cloned object’s data members is mutated, the original object’s data members would also get mutated.This is definitely not ideal.
Clone array members separately to make the clone and the original object independent
The recipe for cloning a class that has array member variables would be to first clone the class, and then set the clone’s array member variables as the clone of the array member variables of the original.
MyClass clone = (MyClass) original.clone();
clone.arrayOne = original.arrayOne.clone();
clone.arrayTwo =original.arrayTwo.clone();
One caveat with the array assignment approach is that it would not work if arrayOne or arrayTwo is a final field. Hence, the developer would be forced to mark some of the fields as non-final if he wants to use the “Cloneable” interface.
Clone array members additionally would not be sufficient if array members contains mutable objects
If array members contain mutable objects, then those contained objects would also need to be cloned.
Copy constructors
The clone framework is fraught with many issues and is not the recommended approach except for very simple classes. The better alternative is to have a Copy constructor, and to have the developer implement class specific clone implementation.
public MyClass(MyClass other) {
this.fieldOne = other.fieldOne;
}
Copy factory
This is the static factory variant of the copy constructor.
public static MyClass newMyClass(MyClass myClass);
Conversion constructor
This is where an instance of a class is copied to create an instance of another class.
public MyClassTwo(MyClass other) {
}
Conversion factory
This is the factory alternative to the conversion constructor where the returned type is different from the type of the object being cloned.
public static MyClassThree copy(MyClass other){
MyClassThree myClassThree = new MyClassThree();
myClassThree.dataOne =other.dataOne;
myClassThree.specificField = “abcd”;
return myClassThree;
}
clone method can be used for arrays but is not recommended in general
When clone functionality needs to be implemented, prefer to use copy constructors or copy factory methods and do not lean on the clone functionality provided by the Java language.