繁体   English   中英

覆盖 Clone 而不调用 super.clone

[英]Overriding Clone without calling the super.clone

在不实现 Cloneable 接口和不调用 super.clone() 的情况下,覆盖clone方法是否是一个好习惯。 这样就不会抛出CloneNotSupportedException异常。

考虑这个类:

class Money {

    private BigDecimal x ;

    public Object clone() {
        Money m1 = new Money();
        m1.setX(this.x);
        return m1;
    }

    public BigDecimal getX() {
        return x;
    }

    public void setX(BigDecimal x) {
        this.x = x;
    }

}        

这个类不会抛出CloneNotSupportedException并且它的工作方式就像一个复制构造函数。

这是一个好方法吗?

不调用super.clone()是不好的做法。 有关如何实现克隆方法的更多信息,请参阅此答案

您有克隆逻辑,当然您知道,您的类支持克隆,因此clone方法不应抛出CloneNotSupportedException 因此,在此处调用super.clone()会导致您编写样板try / catch块,并重新抛出包裹在AssertionError CloneNotSupportedException ,这显然从未被抛出。 而这个标记Cloneable ......我认为,Java 的这一部分设计不当。 所以我只是忽略文档并手动复制字段。

使用super.clone()的唯一两个参数是性能(我想,类似于内部使用的memcpy )和添加新字段时的错误持久性。

在你的情况下,它是可行的,但在某些情况下,它甚至不起作用。 在不调用super.clone情况下实现clone方法将无法使子类 cloneable 例如,假设我有一个Fruit

public class Fruit implements Cloneable {

    private String fruitName;

    public Fruit(String fruitName) {
        this.fruitName = fruitName;
    }

    public String getFruitName() {
        return fruitName;
    }

    public Fruit clone() {
        return new Fruit(fruitName);
    }
}

正如你所看到的,我在没有调用super.clone情况下实现Fruit#clone方法,这看起来很好。 但是现在我想创建一个扩展Fruit的类Apple ,如下所示:

public class Apple extends Fruit {

    private String origin;

    public Apple(String fruitName, String origin) {
        super(fruitName);
        this.origin = origin;
    }

    public Apple clone() {
        // TODO
    }

}

现在的问题是,如何实现苹果的clone方法。 我应该在 Apple 的clone方法中调用super.clone吗? 是的,您应该这样做,否则您将无法获得fruitName的值,该值是Fruit的私有字段。 好的,让我们试试这个:

public Apple clone() {
    return (Apple) super.clone();
}

然而,它没有用。 因为Fruit#clone不调用super.clone ,所以在Apple#clonesuper.clone()的结果返回一个Fruit的实例而不是Apple Fruit不能被扔进Apple ,因此会抛出一个错误:

Exception in thread "main" java.lang.ClassCastException: Fruit cannot be cast to Apple
    at Apple.clone(Apple.java:20)

如您所见,您无法为Apple提供可行的clone方法,无论您是否调用super.clone ,它都不起作用。 只是因为你没有在Fruit#clone调用super.clone

总之,如果您希望子类可clone ,则需要在clone方法中调用super.clone

在您的情况下,您有一个具有clone方法的类,而无需告诉外界它实际上是Cloneable 这不称为克隆,而是原型制作。

如果要clone使用该接口,否则请选择其他方法名称。

还请检查文档:

创建并返回此对象的副本。 “复制”的确切含义可能取决于对象的类别。 一般意图是,对于任何对象 x,表达式:x.clone() != x 将为真,并且表达式:x.clone().getClass() == x.getClass() 将为真,但这些都不是绝对的要求。 虽然通常的情况是: x.clone().equals(x) 为真,但这不是绝对要求。 按照惯例,返回的对象应该通过调用 super.clone 来获取。 如果一个类和它的所有超类(Object 除外)都遵守这个约定,那么 x.clone().getClass() == x.getClass() 就是这种情况。

您必须调用super.clone()是有原因的。

clone 是关于处理/复制对象的类型和状态。 如果我们必须确定类型,并且将它与 Object 类相关联是有意义的。 Java 本质上是通过引用和浅拷贝原理进行操作的。 如果我们必须启用深度复制,那么用户必须在 clone() 中指明运行时特殊处理已完成。 这是通过使用 Clonable 接口进行标记来完成的。

理论上,添加对 clone 的覆盖是强制执行原型模式的正确方法,即在不知道类型的情况下在运行时创建对象的副本。

实际上它是一个不同的故事。 克隆是执行此操作的 API 标准方法。 当您为独立类编写自己的克隆方法时,可以放弃 super.clone 调用并继续。 但这是一种幼稚的做法。 假设您有一个遵循继承机制的类。 想象一下,狮子是食肉动物,因此又是动物。 现在,如果您将 super.clone 放在 Lion 中,您最终可能会重新创建 Lion 的副本,而它既不是食肉动物,也不是上帝禁止的动物。

因此,最好遵循 API 标准并避免出现任何问题。

暂无
暂无

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

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