Effective Java第11条:谨慎地覆盖clone
,仔细阅读后,我们可以更进一步得出结论:其他接口不要继承(extends
扩展)Cloneable
接口,其他类不要实现(implements
)该接口。如果真的需要对象深度(内存)拷贝,可以考虑使用序列化和反序列化代替实现。
对于clone
方法,它本身是浅拷贝,如果拷贝的类持有其他类变量,必须让这个类变量的定义类实现Copy Constructor
。
另一个实现对象拷贝的好办法就是提供一个拷贝构造器(copy constructor
)或者拷贝工厂(copy factory
)。拷贝构造器只是一个构造器,它唯一的参数类型是包含该构造器的类,例如:
public Yum(Yum yum)
拷贝工厂是类似于拷贝构造器的静态工厂:
public static Yum newInstance(Yum yum)
拷贝构造器的做法,及其静态工厂方法的变形,都比Cloneable/clone
方法具有更多的优势,它们不依赖于某一种很有风险的、语言之外的对象创建机制;它们不要求遵守尚未制定好文档的规范;它们不会与final域的正常使用发生冲突;它们不会抛出不必要的受检异常(check exception
);他们不需要进行类型转换。
虽然你不可能把拷贝构造器或者静态工厂放在接口中,但是由于Cloneable
缺少一个公有的clone
方法,所以它也没有提供一个接口该有的特性。因此,使用拷贝构造器或者拷贝工厂来代替clone
方法时,并没有舍弃接口的功能特性。
更进一步,拷贝构造器或者拷贝工厂可以带一个参数,参数类型是通过该类实现的接口。例如,按照惯例,所有通用集合都提供了一个拷贝构造器,它的参数类型为Collection
或者Map
。
基于接口的拷贝构造器和拷贝工厂(更准确说叫’转换构造器conversion constructor
‘)和转换工厂(conversion factory
)),允许客户选择拷贝的实现类型,而不是强迫用户去接受原始的实现类型。例如,假设你有一个HashSet
,并且希望把它拷贝成一个TreeSet
,clone
的方法无法提供这样的功能,但是用转换构造器很容易实现:new TreeSet(s)
。