Java:super.clone()方法和继承

Shu*_*eng 13 java inheritance clone object instance

我有一个关于clone()Java中的方法的快速问题,用于super.clone()继承 - 我clone()从父按钮中一直调用父类中的方法.

clone()方法应该返回该对象的副本,但是如果我在继承heirachy中有三个类并且调用super.clone()三次,为什么继承heirachy中的最高类(仅在类Object下)得到该类的副本回?

假设我们有三个类:A,B和C,其中A - > B - > C(inherit = - >)

然后super.clone()在C类中调用,调用clone()B调用super.clone(),clone()在A中调用,调用super.clone()'this this Object.clone()被调用'.为什么它不是this从A类返回的对象的副本Object.clone()?这听起来很合乎逻辑.

Bob*_*oss 12

听起来这里至少有两个问题在起作用:

  1. 听起来你对clone()通常如何实现感到困惑.

  2. 听起来你认为克隆是一个好主意(与使用复制构造函数,工厂或它们的等价物相比).

以下是克隆方法的实现示例:

@Override 
public Object clone() throws CloneNotSupportedException {   
    //get initial bit-by-bit copy, which handles all immutable fields
    Fruit result = (Fruit)super.clone();

    //mutable fields need to be made independent of this object, for reasons
    //similar to those for defensive copies - to prevent unwanted access to
    //this object's internal state
    result.fBestBeforeDate = new Date( this.fBestBeforeDate.getTime() );

    return result;
}
Run Code Online (Sandbox Code Playgroud)

注意,结果super.clone()立即转换为a Fruit.这允许继承方法然后修改Fruit特定的成员数据(fBestBeforeDate在本例中).

因此,调用子clone()方法虽然它将调用父类的克隆,但也会对新制作的副本添加自己的特定修改.在这种情况下,出来的将是一个Fruit,而不是一个Object.

现在,更重要的是,克隆是一个坏主意.复制构造函数和工厂提供了更直观,易于维护的替代方案.尝试阅读我附加到示例的Java Practices链接上的标题:它总结了一些问题.Josh Bloch还有一个更长的讨论:绝对应该避免克隆.以下是关于他认为克隆是一个问题的原因的优秀摘要段落:

Object的clone方法非常棘手.它基于现场副本,而且是"超语言".它创建一个对象而不调用构造函数.无法保证它保留构造函数建立的不变量.多年来,在Sun内外都存在许多错误,这源于这样一个事实,即如果你只是反复调用super.clone直到克隆了一个对象,那么你就拥有了一个浅层的对象副本.克隆通常与正在克隆的对象共享状态.如果该状态是可变的,则您没有两个独立的对象.如果您修改一个,另一个也会更改.突然之间,你会得到随机行为.

  • -1表示"克隆是一个坏主意".有时候`clone`是有保证的,复制构造函数不起作用.考虑克隆一个对象而不知道运行时的确切类型.在那种情况下,您使用哪个类的复制构造函数?克隆有点难以正确,但如果做得好,它的效果很好.请参阅我关于如何正确实施克隆的另一个克隆问题的答案http://stackoverflow.com/questions/1052340/what-is-wrong-with-this-clone/1053227#1053227 (3认同)

oar*_*ish 5

尽管有一个答案被接受,但我认为它并没有完全回答问题的第一部分(为什么子类中的向下转型总是有效)。虽然我无法真正解释它,但我想我可以澄清一些与我相同的发帖者的困惑。我们有以下课程

class A implements Cloneable 
{
   @Override
   protected A clone() throws CloneNotSupportedException // could be public
   { 
      Object clone = super.clone();
      System.out.println("Class A: " + clone.getClass()); // will print 'C'
      return (A) clone;
   }
}

class B extends A
{
   @Override
   protected B clone() throws CloneNotSupportedException
   { 
      A clone = super.clone();
      System.out.println("Class B: " + clone.getClass()); // will print 'C'
      return (B) clone;
   }
}

class C extends B
{
   @Override
   protected C clone() throws CloneNotSupportedException
   { 
      B clone = super.clone();
      System.out.println("Class C: " + clone.getClass()); // will print 'C'
      return (C) clone;
   }
}

static main(char[] argv)
{
   C c = new C();
   C cloned_c = c.clone();
}
Run Code Online (Sandbox Code Playgroud)

这样做的结果是

Class A: C

Class B: C

Class C: C

打印在命令行上。因此,事实上,该clone()方法Object可以以某种方式查看调用堆栈并查看调用链开头的clone()哪种类型的对象,然后,只要调用冒泡Object#clone(),以便实际调用该类型的对象被建造。所以这种情况已经在 class 中发生了C,这很奇怪,但它解释了为什么向下转型不会导致ClassCastException. 我检查过 OpenJDK,看来这是由一些在本机代码中实现的 Java 黑魔法造成的。