【问题标题】:Is the Java clone() method the only way to achieve polymorphic cloning?Java clone() 方法是实现多态克隆的唯一方法吗?
【发布时间】:2013-03-25 06:51:20
【问题描述】:

我需要为我的班级配备多态克隆(深拷贝),即我需要这样的东西才能工作:

SuperType original = new SubType();
SuperType copy = original.clone();

其中original.clone()可以被任何机制替换以创建深拷贝,copy的实际类型应为SubType,因为original也是SubType

clone() 方法和Cloneable 接口是实现此目的的唯一方法吗?工厂方法和复制构造函数不能被使用,因为实际的类只在运行时才知道,对吧?除了那些 serialize-deserialize 方法和 Java deep-cloning library 之外还有其他建议的方法吗? 恕我直言,它比 clone() 方法更糟糕?

谢谢,彼得

【问题讨论】:

  • 你不能做一个你想被克隆的对象可以传递给的构造函数?
  • @gangqinlaohu 当然,我可以,但是我需要打电话给new SubType(original),因为我在编写代码时不知道原始是否真的属于SubType,或者SubType2,或SubSubType

标签: java clone deep-copy cloneable


【解决方案1】:

实际上 Object 的 clone() 方法不允许您进行任何多态调用,因为它是 protected。实现Cloneable 也没有用,因为它不包含clone() 方法。

您可以通过为克隆的类提供多态方法来进行多态克隆,该方法反过来调用复制构造函数。

abstract class SuperType {
    public SuperType(SuperType t) {
    }

    public abstract SuperType deepCopy();
}

class SomeType extends SuperType {

    SomeType(SomeType t) {
        //copy constructor
    }

    @Override
    public SomeType deepCopy() {                        
        return new SomeType(this);
    }
}

 ...

SuperType original = new SubType();
SuperType copy = original.deepCopy(); //the deepCopy is called on children classes

另见:

Joshua Block's opinion on cloning vs. copy constructor

【讨论】:

  • 好的,那行得通。但这与使用clone() 有何不同? deepCopy() 方法不只是它的不同名称吗?你在deepCopy() 做的事情不能在clone() 做吗?
  • @Posa Cloning 是一种 java“内置”方式,无需复制构造函数即可提供对象副本。它试图让程序员的生活更轻松,但 IMO 并非如此。是的,如果您在覆盖 clone() 时非常小心(Joshua Block 的 Effectiva Java 有一整章介绍了它),您应该会得到相同的结果。
【解决方案2】:

为了使您的代码能够正常工作,该代码要么位于SuperType 类或子类中,要么您的SuperType 类型必须具有公共clone() 方法。公共clone() 方法不会自动存在,您必须实现它。就此而言,您可以将其称为其他任何名称,例如copy().

那么,您的问题是如何实现 clone()(或您所称的任何名称)方法,以便进行多态克隆。

Java 开发人员的意图是调用super.clone(),假设继承层次结构中的所有类都类似地实现clone() 来执行多态克隆。这最终得到了受保护的Object.clone() 方法,它具有多态克隆的魔力。请注意,要使Object.clone() 不引发异常,您的类必须实现Cloneable 接口。

但是,还有其他可能的方法。例如,假设所有子类都有一个默认构造函数,您可以执行this.getClass().newInstance() 之类的操作。这将创建正确类的对象,但不会复制字段。你的 clone 方法需要复制所有的字段,子类必须重写你的 clone 方法来复制它们的字段等。注意在这种情况下是否实现Cloneable 接口是无关紧要的。

另一种方法是,假设类是可序列化的,对this 进行序列化和反序列化。这应该会创建一个多态克隆,其中包含所有可序列化的字段。

【讨论】:

    【解决方案3】:

    你可以简单地为 SuperType 添加一个复制方法并在所有 SubTypes 中实现它

    class SuperType {
    
        public SuperType copy() {
                  ...
        }
    }
    

    【讨论】:

      【解决方案4】:

      如果您不喜欢Cloneable 界面,您可以创建自己的界面。每个类都将负责创建正确类型的新实例。

      【讨论】:

      • 这不是因为我不喜欢Clonable。我不明白不要使用克隆恐慌(尽管我读过 J. Bloch 的书,但可能我不太了解与 clone 相关的问题)。为什么要设计我自己的界面基本上与Clonable相同的目的?或者我自己的界面的目的会和Clonable的不同吗?
      • 嗯,您的问题是“clone() 方法和 Cloneable 接口是实现此目的的唯一方法吗?”。如果您可以使用 clone() 方法,请使用它。但是,使用自定义接口可以让您利用泛型。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-26
      • 2010-10-07
      • 2017-08-10
      • 1970-01-01
      • 2012-09-14
      • 2012-12-18
      相关资源
      最近更新 更多