Effective Java claims that elements.clone() suffices

  • A+
Category:Languages

I'm reading on Joshua Bloch's Effective Java, 2nd edition, Item 11: Override clone judiciously.

On page 56, he is trying to explain that when we override clone() for some classes (like collection classes), we must copy the internals of it. He then gives the example of designing a class Stack:

public class Stack {     private Object[] elements;     private int size = 0;     private static final int DEFAULT_INITIAL_CAPACITY = 16;     public Stack() {...}     public void push(Object e) {...}     public Object pop() {...}     private void ensureCapacity() {...} //omitted for simplicity } 

He claims that if we simply use super.clone() to clone a Stack, the resulting Stack instance "will have the correct value in its size field, but its elements field will refer to the same array as the original Stack instance. Modifying the original will destroy the invariants in the clone and vice versa. You will quickly find that your program produces nonsensical results or throws a NullPointerException." Now that seems fair. But he then gives an example of the "correct implementation", which confuses me:

@Override public Stack clone() {     try {         Stack result = (Stack) super.clone();         result.elements = elements.clone();         return result;     } catch (CloneNotSupportedException e) {         throw new AssertionError();     } } 

Now how is that different from super.clone()? I know, the new Stack.element will be a different reference than the old one and all; but the "internals" of the array are still the same, aren't they? The actual elements of the array result.element still point to the original Object references. That could still result in destroying the invariants of the clone when changing the original, or vice versa, couldn't it? Am I missing anything?

 


You are absolutely right about how clone works. The objects in the backing array will not be copied, but the backing array will be copied.

That is not a problem because the caller is not expecting the elements to be copied anyway. For collection classes like stacks, the "norm" is to do a shallow copy. One example from the standard library is the copy constructor of ArrayList.

Also note that you could implement clone by cloning the objects inside the array as well (this would mean that the stack can only store Clonable objects that expose clone). That would not break the contract of clone. The contract is very loose.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: