我在读约书亚布洛赫的有效的Java,第二版明智覆盖克隆:11项。
在56页,他试图解释,当我们重写clone()
了一些类(比如集合类),我们必须复制它的内部。然后,他给了设计一个类的实例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
}
他声称,如果我们简单地使用super.clone()
克隆Stack
,所产生的协议栈实例“将在它的大小字段中输入正确的值,但它的元素域将指向同一个数组作为原始栈实例。修改原来会破坏不变的克隆,反之亦然。你很快就会发现,你的程序产生无意义的结果或抛出一个NullPointerException“。现在,似乎是公平的。但他接着给出了“正确执行”,这让我困惑的一个例子:
@Override public Stack clone() {
try {
Stack result = (Stack) super.clone();
result.elements = elements.clone();
return result;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
现在是如何从不同super.clone()
?我知道,新的Stack.element
会比旧的和所有不同的参考; 但数组的“内部”仍然是相同的,不是吗?数组的实际元素result.element
仍指向原始Object
引用。这可能还导致改变原来的,反之当反之亦然销毁克隆的不变量,不能呢?我错过了什么?
你是绝对正确的关于如何clone
工作。背衬数组中的对象将不被复制,但是背衬阵列将被复制。
这不是一个问题,因为主叫方没有预料的元素被无论如何都会被复制。对于集合类,如堆栈,该“规范”是做一个浅拷贝。从标准库的一个例子是拷贝构造函数ArrayList
。
另外请注意,您可以实现clone
通过克隆阵列内的对象,以及(这将意味着堆栈只能存储Clonable
暴露的对象clone
)。这将不会打破的合同clone
。该合同是非常宽松的。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句