谨慎地覆盖clone

谨慎地覆盖clone

概述

Cloneable接口没有任何方法,它表明object允许被clone。这个接口的作用是改变clone方法的行为,使得Object中的clone方法返回对象的逐域拷贝,否则CloneNotSupportedException异常。

Object的clone方法是protected的。

约定

如果实现Cloneable接口是要对某个类起作用,那该类和它的所有超类都必须满足:无需调用构造器就可以创建对象。并且这个拷贝应该满足:

x.clone() != x 为true

x.clone().getClass() == x.getClass() 为true

x.clone().equals(x) 为true

实际上,良好的clone方法可以调用构造器创建对象,之后再去复制内部数据。

super.clone()

假设超类提供了良好的clone方法,那么从super.clone()中得到的对象可能会接近于最终返回的对象,也可能相距甚远。如果每个域包含一个基本类型的值,或者包含一个指向不可变对象的引用,那么返回对象就可能是符合要求的。

例如

1
2
3
4
5
6
7
public PhoneNumber clone() {
try {
return (PhoneNumber) super.clone();
} catch(CloneNotSupportedException e) {
throw new AssertionError();
}
}

注意,clone方法返回的是PhoneNumber,永远不要让那个客户去做类库能替用户完成的事情。

注意

如果对象中包含的域引用了可变的对象,那这种clone实现可能导致灾难性后果。因为修改原始的实例可能会破坏克隆对象的约束条件。

复杂对象的克隆,需要先调用super.clone,再把结果对象中的所有域设置为空白状态,然后调用higher level去重新设置对象状态。

跟构造器一样,clone不应该调用一个被覆盖的方法,因为该方法可能先执行,导致克隆对象和原始对象不一致。

总结

所有实现了Cloneable接口的类都应该用一个公有的方法去覆盖clone:先调用super.clone,然后修正需要的域,这意味着需要拷贝任何包含内部深层结构的可变对象。

另一个实现对象拷贝的方法是提供一个拷贝构造器或者拷贝工厂。

对于一个专门用于继承而设计的类,如果未能提供行为良好的protected的clone方法,那么它的子类就不可能实现Cloneable接口。