Skip to main content

"Cloneables" Should Implement clone()

Medium
robustnessconventions

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
}
}
}