"Cloneables" Should Implement clone()
What is it?
This practice addresses the use of the Cloneable
interface in Java without overriding the clone()
method. Implementing Cloneable
without providing a custom clone()
method may lead to unexpected behavior and violations of the Cloneable
contract.
Why apply it?
When a class claims to be Cloneable
but does not override Object.clone()
, it may not adhere to the expected semantics of a deep or shallow copy, potentially causing bugs and maintenance problems. The expected behavior when implementing Cloneable
is that calling clone()
will not throw a CloneNotSupportedException
.
How to fix it?
Override the clone()
method in any class that implements Cloneable
. Ensure that it calls super.clone()
and properly handles copying, either shallow or deep, depending on the class's needs.
Examples
Example 1:
Negative
A noncompliant example where the class implements Cloneable
but does not override clone()
.
class Foo implements Cloneable {
public int value;
// Missing clone method
}
Example 2:
Positive
A compliant example where the class properly implements the clone()
method, ensuring the object can be cloned safely and predictably.
class Foo implements Cloneable {
public int value;
@Override
public Foo clone() {
try {
return (Foo) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError(); // Can't happen because Foo is Cloneable
}
}
}
Negative
The negative example shows an incomplete or incorrect cloning implementation, which overlooks overriding clone()
when implementing Cloneable
.
import java.util.List;
class Entity implements Cloneable {
public int id;
public List<Entity> children;
// Does not override clone method
}
Example 3:
Positive
This positive example demonstrates a class implementing Cloneable
and performing additional copying logic in the clone()
method to achieve a deep copy.
import java.util.List;
import java.util.ArrayList;
class Entity implements Cloneable {
public int id;
public List<Entity> children;
@Override
public Entity clone() {
try {
Entity clone = (Entity) super.clone();
clone.children = new ArrayList<>(children.size());
for (Entity child : children) {
clone.children.add(child.clone());
}
return clone;
} catch (CloneNotSupportedException e) {
throw new AssertionError(); // Shouldn't happen
}
}
}