(Item 10) Override clone() judiciously
General Contract
x.clone() != x
x.clone().getClass() == x.getClass()
x.clone().equal(x) == true
In practice, a class that implements Cloneable is expected to provide a properly functioning public method.
1) If every field contains a primitive value or a reference to an immutable object
public Object clone() {
try {
return super.clone();
} catch(CloneNotSupportedException e) {
throw new Error("Assertion failure"); // Can't happen
}
}
2) If, however, your object contains fields that refer to mutable objects
public class Stack {
private Object[] elements;
private int size = 0;
public Stack(int initialCapacity) {
this.elements = new Object[initialCapacity];
}
public void push(Object e) {
ensureCapacity();
elements[size++] = e;
}
public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; // Eliminate obsolete reference
return result;
}
// Ensure space for at least one more element.
private void ensureCapacity() {
if (elements.length == size) {
Object oldElements[] = elements;
elements = new Object[2 * elements.length + 1];
System.arraycopy(oldElements, 0, elements, 0, size);
}
}
}
//In effect, the clone method functions as another constructor; you must ensure that it does no harm to
//the original object and that it properly establishes invariants on the clone
public Object clone() throws CloneNotSupportedException {
Stack result = (Stack) super.clone();
result.elements = (Object[]) elements.clone();
return result;
}
3) It is not always sufficient to call clone() recursively
suppose you are writing a clone method for a hash table whose internals consist of an array of buckets,
each of which references the first entry in a linked list of key-value pairs or is null if the bucket is empty
public class HashTable implements Cloneable {
private Entry[] buckets = ...;
private static class Entry {
Object key;
Object value;
Entry next;
Entry(Object key, Object value, Entry next) {
this.key = key;
this.value = value;
this.next = next;
}
// Recursively copy the linked list headed by this Entry
Entry deepCopy() {
return new Entry(key, value,
next == null ? null : next.deepCopy());
}
}
public Object clone() throws CloneNotSupportedException {
HashTable result = (HashTable) super.clone();
result.buckets = new Entry[buckets.length];
for (int i = 0; i < buckets.length; i++)
if (buckets[i] != null)
result.buckets[i] = (Entry)
buckets[i].deepCopy();
return result;
}
... // Remainder omitted
}
Use clone as little as possible, try to use copy constructor/static factory instead of clone()
For example: java.util.ArrayList contains a copy constructor : ArrayList(Collection c)