简体   繁体   English

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

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

I have a quick question regarding the clone() method in Java, used as super.clone() in regard to inheritance - where I call the clone() method in the parent class all the way up from the button. 我有一个关于Java中clone()方法的快速问题,在继承方面用作super.clone() - 我在父类中从按钮一直调用clone()方法。

The clone() method is supposed to return a copy of this object, however if I have three classes in an inheritance heirachy and call super.clone() three times, why doesn't the highest class in the inheritance heirachy, just under class Object, get a copy of that class returned? clone()方法应该返回这个对象的副本,但是如果我在继承heirachy中有三个类并且调用super.clone()三次,为什么不继承heirachy中的最高类,就在类下面对象,获取该类返回的副本?

Suppose we have three classes: A, B and C, where A -> B -> C (inherit = ->) 假设我们有三个类:A,B和C,其中A - > B - > C(inherit = - >)

Then calling super.clone() in class C, invokes clone() in B which calls super.clone() , invoke clone() in A which call super.clone() 'this time Object.clone() gets called'. 然后调用super.clone()在C类,调用clone()在B中调用super.clone() ,调用clone()在A中调用super.clone() '这个时候Object.clone()被调用'。 Why is it not a copy of the this object with respect to class A that gets returned from Object.clone() ? 为什么它不是从Object.clone()返回的类A的this对象的副本? That sounds logical to me. 这听起来很合乎逻辑。

It sounds like there are at least two problems at work here: 听起来这里至少有两个问题在起作用:

  1. It sounds like you're confused about how clone() normally gets implemented. 听起来你对clone()通常如何实现感到困惑。

  2. It sounds like you're thinking that cloning is a good idea (vs. using a copy constructor, factories or their equivalent). 听起来你认为克隆是一个好主意(与使用复制构造函数,工厂或它们的等价物相比)。

Here is an example of an implementation of a clone method: 以下是克隆方法的实现示例

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

Note that the result of super.clone() is immediately cast to a Fruit . 请注意, super.clone()的结果会立即转换为Fruit That allows the inheriting method to then modify the Fruit-specific member data ( fBestBeforeDate in this case). 这允许继承方法然后修改Fruit特定的成员数据(在本例中为fBestBeforeDate )。

Thus, the call to a child clone() method, while it will call the parents' clones, also adds its own specific modifications to the newly made copy. 因此,调用子clone()方法虽然它将调用父类的克隆,但也会对新制作的副本添加自己的特定修改。 What comes out, in this case, will be a Fruit , not an Object . 在这种情况下,出来的将是一个Fruit ,而不是一个Object

Now, more importantly, cloning is a bad idea . 现在,更重要的是, 克隆是一个坏主意 Copy constructors and factories provide much more intuitive and easily maintained alternatives. 复制构造函数和工厂提供了更直观,易于维护的替代方案。 Try reading the header on the Java Practices link that I attached to the example: that summarizes some of the problems. 尝试阅读我附加到示例的Java Practices链接上的标题:它总结了一些问题。 Josh Bloch also has a much longer discussion : cloning should definitely be avoided. Josh Bloch还有一个更长的讨论 :绝对应该避免克隆。 Here is an excellent summary paragraph on why he thinks cloning is a problem: 以下是关于他认为克隆是一个问题的原因的优秀摘要段落:

Object's clone method is very tricky. Object的clone方法非常棘手。 It's based on field copies, and it's "extra-linguistic." 它基于现场副本,而且是“超语言”。 It creates an object without calling a constructor. 它创建一个对象而不调用构造函数。 There are no guarantees that it preserves the invariants established by the constructors. 无法保证它保留构造函数建立的不变量。 There have been lots of bugs over the years, both in and outside Sun, stemming from the fact that if you just call super.clone repeatedly up the chain until you have cloned an object, you have a shallow copy of the object. 多年来,在Sun内外都存在许多错误,这源于这样一个事实,即如果你只是反复调用super.clone直到克隆了一个对象,那么你就拥有了一个浅层的对象副本。 The clone generally shares state with the object being cloned. 克隆通常与正在克隆的对象共享状态。 If that state is mutable, you don't have two independent objects. 如果该状态是可变的,则您没有两个独立的对象。 If you modify one, the other changes as well. 如果您修改一个,另一个也会更改。 And all of a sudden, you get random behavior. 突然之间,你会得到随机行为。

Although one answer is accepted, I do not think it fully answers the first part of the question (why downcasting in subclasses always works). 虽然有一个答案被接受,但我认为它不能完全回答问题的第一部分(为什么子类中的向下转换始终有效)。 Although I cannot really explain it, I think I can clear up some of the poster's confusion which was the same as mine. 虽然我无法解释它,但我想我可以清理一些与我的相同的海报混乱。 We have the following classes 我们有以下课程

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

The result of this is that 结果就是这样

Class A: C

Class B: C

Class C: C

is printed on the command line. 在命令行上打印。 So, as a matter of fact, the clone() method of Object somehow can look down the call stack and see which type of object at the start of the chain invoked clone() , then, provided the calls bubble up so that Object#clone() is actually called, an object of that type is created. 所以,事实上, Objectclone()方法可以向下查看调用堆栈并查看链的开头调用clone()哪种类型的对象,然后,如果调用冒泡,那么Object#clone()实际上调用了Object#clone() ,创建了该类型的对象。 So this happens already in class C , which is strange, but it explains why the downcasts do not result in a ClassCastException . 所以这已经发生在C类中,这很奇怪,但它解释了为什么向下转换不会导致ClassCastException I've checked with the OpenJDK, and it appears this comes by some Java black magic implemented in native code. 我已经检查过OpenJDK,看起来这是由一些在本机代码中实现的Java黑魔法。

Its a special native method. 它是一种特殊的原生方法。 This was done to make cloning easier. 这样做是为了使克隆更容易。 Otherwise you will have to copy the whole code of your ancestor classes. 否则,您将必须复制祖先类的整个代码。

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

this class has information about this class as well as it is associated with another class. 这个类有关于这个类的信息,并且它与另一个类相关联。 So, conceptually this class's object will have information of associated class as well. 因此,从概念上讲,该类的对象也将具有关联类的信息。 this object is incomplete object without associated object/parent class.all direct as well as indirect fields in this class needs to be copied make it a worth it new clone of current object. 这个对象是不完整的对象,没有关联的对象/父类。这个类中的所有直接和间接字段都需要被复制,这使得它值得当前对象的新克隆。 we cannot access only that portion of reference which only denotes child section. 我们不能仅访问仅表示子节的那部分参考。

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

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