繁体   English   中英

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

[英]Java: super.clone() method and inheritance

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

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

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

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

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

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

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

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

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

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

虽然有一个答案被接受,但我认为它不能完全回答问题的第一部分(为什么子类中的向下转换始终有效)。 虽然我无法解释它,但我想我可以清理一些与我的相同的海报混乱。 我们有以下课程

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()实际上调用了Object#clone() ,创建了该类型的对象。 所以这已经发生在C类中,这很奇怪,但它解释了为什么向下转换不会导致ClassCastException 我已经检查过OpenJDK,看起来这是由一些在本机代码中实现的Java黑魔法。

它是一种特殊的原生方法。 这样做是为了使克隆更容易。 否则,您将必须复制祖先类的整个代码。

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM