我了解Java的克隆机制以及不建议使用它的原因。但是假设我仍然想要使用它。
为了方便起见,我想编写一个类Foo的克隆方法,如下所示:
@Override
public Foo clone(){
Foo f = (Foo)super.clone();
//Replace mutable fields
return f;
}
据我所知,这样做应该是安全的。但是,我注意到API中的Cloneable类没有这样做。这是有原因的吗?这样做是否不好?
我了解Java的克隆机制以及不建议使用它的原因。但是假设我仍然想要使用它。
为了方便起见,我想编写一个类Foo的克隆方法,如下所示:
@Override
public Foo clone(){
Foo f = (Foo)super.clone();
//Replace mutable fields
return f;
}
据我所知,这样做应该是安全的。但是,我注意到API中的Cloneable类没有这样做。这是有原因的吗?这样做是否不好?
在Java 5之前,协变返回类型根本不被允许,因此 Foo clone()
不会通过编译。
由于很多实现了 Cloneable
接口的类已经很久以前写好了,这可能是大多数情况的原因。
我怀疑较新的类之所以不使用它,只是因为众所周知的 clone()
非常麻烦,当人们很少使用它时,他们甚至不会想到可以进行类型转换。他们会找到一些“如何实现clone()” 的问题,并用老式方式编写代码。
Object.clone()
创建一个新对象,其中所有字段的值都与原始对象相同。对于引用类型的字段,这意味着新字段只是原始对象所引用对象的引用。它不会克隆字段。因此,在许多情况下,默认实现会导致共享数据,这可能是不希望发生的。例如,如果 ArrayList.clone()
使用默认实现,你将得到两个共享相同后备数组的 ArrayList
实例。super.clone()
的结果简单地转换为适当的类型可能是个好主意。clone()
的具体预期操作并没有明确说明,但它指出:通常,这意味着复制组成被克隆对象的内部“深层结构”的任何可变对象,并使用对这些对象副本的引用替换引用。
,因此clone()
应该理想情况下返回一个深拷贝。但是理想情况和必然情况不同... - biziclop(MyClass) super.clone()
的原因是这是一个浅拷贝,而通常需要深拷贝。我已经说过,在深/浅拷贝之间没有区别的情况下可以这样做。 - Paul Boddingtonclone()
时不使用协变返回类型,而不是关于强制转换super.clone()
的问题。 - biziclop嗯,实际上这不是一种糟糕的风格,自从Java 1.5版本发布以来,协变返回类型已作为泛型的一部分引入,因此这样做可以使您的API更易于使用(无需强制转换)。
来源(《Effective Java 第2版》)。