item13 · 谨慎地覆盖 clone
Cloneable
接口的目的是作为对象的一个 mixin 接口,表明这样的对象允许克隆。但,它并没有成功地达到这个目的,它的主要缺陷在于缺少一个 clone 方法,而 Object
的 clone 方法是受保护的。
Cloneable
作用
- 决定了
Object
中受保护的 clone 方法实现的行为- 如果一个类实现了
Cloneable
接口,Object
的 clone 方法就返回该对象的逐域拷贝 - 如果一个类没有实现
Cloneable
接口,就会抛出CloneNotSupportedException
- 如果一个类实现了
- 对于
Cloneable
接口,它改变了超类中受保护的方法的行为。不值得效仿 - 事实上,实现
Cloneable
接口的类是为了提供一个功能适当的公有的 clone 方法。
clone 方法规范
-
为了达到上述目的,类及其所有超类都必须遵守一个相当复杂的、不可实施的、并且基本上没有文档说明的协议。由此得到一种语言之外(依赖底层实现)的机制:它无需构造器就可以创建对象。
-
clone 方法的通用约定是非常弱的,下面是摘自
Object
规范中的约定内容Creates and returns a copy of this object. The precise meaning of “copy” may depend on the class of the object. The general intent is that, for any object
x
, the expression:x.clone() != x
will be true, and that the expression:
x.clone().getClass() == x.getClass()
will be true, but these are not absolute requirements.
While it is typically the case that:x.clone().equals(x)
will be true, this is not an absolute requirement.
By convention, the returned object should be obtained by calling
super.clone
. If a class and all of its super-classes (except Object) obey this convention, it will be the case thatx.clone().getClass()
==x.getClass()
. -
注意:如果类的 clone 方法返回的实例不是通过调用
super.clone()
方法获得,而是通过调用构造器获得,编译器不会发出警告,但是该类的子类调用了super.clone()
方法,得到的对象是超类的,并不是子类的。 -
不可变的类永远都不应该提供 clone 方法
-
Cloneable
结构与引用可变对象的 final 域的正常做法是不相兼容的。 -
clone 方法是浅拷贝(只拷贝一层),对类所引用的对象需手动拷贝
更好的做法
-
提供一个拷贝构造器或拷贝工厂来代替 clone 方法
// Copy constructor public Yum(Yum yum) { ... };
// Copy factory public static Yum newInstance(Yum yum) { ... };
总结
- 既然所有的问题都与
Cloneable
接口有关,新的接口就不应该扩展这个接口,新的可扩展的类也不应该实现这个接口。复制功能最好有构造器或工厂提供。(除数组)