【问题标题】:Java: super.clone() method and inheritanceJava:super.clone() 方法和继承
【发布时间】:2012-08-10 16:22:48
【问题描述】:

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

clone()方法应该返回这个对象的一个​​副本,但是如果我在一个继承heirachy中有三个类并调用super.clone() 3次,为什么继承heirachy中的最高类没有,就在下面类对象,获取该类返回的副本?

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

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

【问题讨论】:

标签: java object inheritance clone instance


【解决方案1】:

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

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

  2. 听起来您认为克隆是个好主意(与使用复制构造函数、工厂或它们的等效项相比)。

这里是an example of an implementation的一个克隆方法:

@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;
}

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

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

现在,更重要的是,克隆是个坏主意。复制构造函数和工厂提供了更直观且易于维护的替代方案。尝试阅读我附加到示例的Java Practices 链接上的标题:它总结了一些问题。 Josh Bloch 也有a much longer discussion:绝对应该避免克隆。以下是关于他为什么认为克隆是一个问题的精彩摘要段落:

Object 的克隆方法非常棘手。它基于现场副本,并且 它是“语言外的”。它创建一个对象而不调用 构造函数。不能保证它保留不变量 由建设者建立。已经有很多错误了 年,无论是在太阳内外,都源于这样一个事实,如果你 只需重复调用 super.clone 直到你克隆了一个 对象,你有一个对象的浅拷贝。克隆一般 与被克隆的对象共享状态。如果该状态是可变的, 你没有两个独立的对象。如果你修改一个,另一个 变化也是如此。突然之间,你得到了随机行为。

【讨论】:

  • 谢谢,我正在阅读的书——Walter Savitch 的 Absolute Java 实际上实现了类似的克隆和复制构造函数——它们都调用了一些 copyOf 方法(私有),返回对克隆的引用。会看看你的论文:)
  • 我的意思是更多,既然 A 类调用 super.clone() 来调用 Object.clone(),为什么不只复制这个对象中属于 A 类的部分,这样我有一个残缺的对象,只有 A 类的属性。当我从 C 类调用 super.clone() 时,this 指针是否会一直转移到层次结构上,直到 super.clone @ class A,这样 Object.clone 知道“这个”对象我必须克隆??
  • -1 表示“克隆是个坏主意”。有时clone 是有保证的,而复制构造函数不起作用。考虑克隆一个对象并且在运行时不知道确切的类型。在那种情况下,您使用哪个类的复制构造函数?克隆有点棘手,但如果做得好,效果很好。请参阅我对如何正确实施克隆的另一个克隆问题的回答stackoverflow.com/questions/1052340/…
  • @SteveKuo,你可能错过了我建议“复制构造函数和工厂”的其余部分。制作多态工厂非常容易。当涉及到克隆时,我主要担心的一个问题是我希望层次结构中的每个其他班级都能正确实施克隆(例如,没有捷径的浅拷贝)。
  • @NicolasLykkeIversen,再次检查示例:首先是对 super.clone() 的调用,该调用被强制转换为子类。然后将数据添加到该子类。然后你把孩子还回去。
【解决方案2】:

它是一种特殊的本地方法。这样做是为了使克隆更容易。否则你将不得不复制你祖先类的全部代码。

【讨论】:

    【解决方案3】:

    虽然接受了一个答案,但我认为它不能完全回答问题的第一部分(为什么子类中的向下转换总是有效的)。 虽然我不能真正解释它,但我想我可以澄清一些与我相同的海报的困惑。 我们有以下课程

    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();
    }
    

    这样做的结果是

    Class A: C

    Class B: C

    Class C: C

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

    【讨论】:

      【解决方案4】:

      如果 B 中的 clone() 返回 A 中的 clone() 返回的内容,C 中的 clone() 返回 B 中的 clone() 返回的内容,则 C 中的 clone() 将返回 A 中的 clone() 返回的内容。

      【讨论】:

      • 我的意思是更多,既然 A 类调用 super.clone() 来调用 Object.clone(),为什么不只复制这个对象中属于 A 类的部分,这样我有一个残缺的对象,只有 A 类的属性。当我从 C 类调用 super.clone() 时,this 指针是否会一直转移到层次结构上,直到 super.clone @ class A,这样 Object.clone 知道“这个”对象我必须克隆??
      【解决方案5】:

      这个类有关于这个类的信息,并且它与另一个类相关联。因此,从概念上讲,这个类的对象也将具有关联类的信息。此对象是不完整的对象,没有关联的对象/父类。需要复制此类中的所有直接和间接字段,使其成为当前对象的值得新克隆。我们不能只访问仅表示子部分的引用部分。

      【讨论】:

        猜你喜欢
        • 2012-04-25
        • 2021-08-22
        • 2014-10-04
        • 2019-06-21
        • 2012-10-18
        • 2017-01-07
        • 2011-02-27
        • 2010-11-20
        • 2017-03-16
        相关资源
        最近更新 更多