19

考虑以下来自 Effective Java Item 11(明智地覆盖克隆)的内容,其中 Josh Bloch 解释了clone()合同的问题。

这份合同存在许多问题。“不调用构造函数”的规定太强了。行为良好的克隆方法可以调用构造函数来创建正在构建的克隆内部的对象。如果类是 final 的,clone 甚至可以返回一个由构造函数创建的对象。

final有人可以通过“如果类是,clone甚至可以返回由构造函数创建的对象”来解释 Josh Bloch 在第一段中所说的话。final跟这里有什么关系clone()

4

5 回答 5

22

这是因为 clone() 的典型实现如下所示:

public class MyClass implements Cloneable {
  protected Object clone() {
    MyClass cloned = (MyClass) super.clone();
    // set additional clone properties here
  }
}

通过这种方式,您可以从您的超类继承克隆行为。人们普遍认为 clone() 操作的结果将根据调用它的对象返回正确的实例类型。IE。this.getClass()

因此,如果一个类是 final 的,您不必担心调用 super.clone() 的子类并没有得到正确的对象类型。

public class A implements Cloneable {
    public Object clone() {
       return new A();
    }
}


public class B extends A {
    public Object clone() {
       B b = (B)super.clone(); // <== will throw ClassCastException
    }
}

但是,如果 A 是最终的,则没有人可以扩展它,因此使用构造函数是安全的。

于 2012-07-18T12:09:39.427 回答
21

如果一个类不是最终类,clone则必须返回调用它的最派生类。这不能与构造函数一起使用,因为clone不知道要调用哪个。如果一个类是 final 的,它就不能有任何子类,所以在克隆时调用它的构造函数是没有危险的。

于 2012-07-18T12:06:58.873 回答
7

一个类不必clone为了可克隆而提供它自己的实现。它可以将其委托给它的可克隆超类。问题来了:clone必须始终返回与调用它的实例相同的类的实例。如果调用显式构造函数,则在所描述的情况下这是不可能实现的。clone另一方面,如果类覆盖是最终的,那就没问题了。

于 2012-07-18T12:09:03.880 回答
2

合同clone规定“按照惯例,返回的对象应该通过调用来获取super.clone”。如果你的类不是最终的并且你返回了通过构造函数调用获得的东西,那么super.clone()从子类调用将不会返回预期的结果(首先,返回的对象的类型不会是子类的类型,作为原生clone()方法会回来)。

于 2012-07-18T12:06:57.690 回答
2

请参阅 Jorado 答案。这就是解释。在最终字段中的其他克隆有问题,请参阅: http ://en.wikipedia.org/wiki/Clone_%28Java_method%29#clone.28.29_and_final_fields

您还应该阅读 Josh 对克隆的采访: http ://www.artima.com/intv/bloch13.html

于 2012-07-18T12:11:16.790 回答