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?
The object cloning is a way to create exact copy of an object. The clone() method of Object class is used to clone an object. The java. lang. Cloneable interface must be implemented by the class whose object clone we want to create.
clone() method returns a Shallow Copy. In shallow copy, if the field value is a primitive type, it copies its value; otherwise, if the field value is a reference to an object, it copies the reference, hence referring to the same object.
Because, for a class to be cloned, you need to implement the Cloneable interface. And then your class uses the clone method of Object class instead. Because, Cloneable interface doesn't exactly have any method for cloning . It would be a better option to use Copy Constructor instead.
From Java 1.5 onwards an overriding method can return subclass of return type declared in original method, which means you can return sub class from clone method. It is known as co-variant method overriding.
clone () method has no side-effects in case of primitives instance variables, as a new object is created during cloning. clone () method if not implemented properly, too has side-effects in case of objects as instance variables, as a cloned object to has a copy of references.
1 Every class that implements clone () should call super.clone () to obtain the cloned object reference. 2 The class must also implement java.lang.Cloneable interface whose object clone we want to create otherwise it will throw CloneNotSupportedException when clone method is called on that class’s object. 3 Syntax:
Those applications which override the clone method can also throw this type of exception to indicate that an object couldn’t or shouldn’t be cloned.
Return Value: The function returns the cloned Object which is the shallow copy of this Properties instance. Below programs illustrate the Java.util.Properties.clone () method.
Now how is that different from super.clone()?
Because the arrays are now different. If two Stack
s share the same array then, when one adds or removes from the stack, the size
field in the other Stack
is not updated, leading to discrepancies.
The array's objects are not cloned themselves. This is deliberate as they do not need to be cloned. It is expected that two Stack
s - or indeed any two Collection
s - can contain references to the same objects. You would get the same behaviour with this code:
Foo foo = new Foo()
Stack stackOne = new Stack();
Stack stackTwo = new Stack();
stackOne.push(foo);
stackTwo.push(foo);
It's not inherently a problem, and is usually the desirable behaviour.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With