覆盖克隆而不调用super.clone

时间:2013-12-02 09:49:49

标签: java 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,它就像复制构造函数一样。

这是一个很好的方法吗?

6 个答案:

答案 0 :(得分:2)

不要拨打super.clone()是不好的做法。有关如何实现克隆方法的更多信息,请参阅this answer

答案 1 :(得分:1)

在你的情况下,它是可行的,但在某些情况下,它甚至不起作用。 在不调用 clone 的情况下实现 super.clone 方法将使子类无法克隆。例如,假设我有一个 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);
    }
}

如您所见,我在不调用 Fruit#clone 的情况下实现了 super.clone 方法,这看起来不错。但是现在我想创建一个扩展 Apple 的类 Fruit,如下所示:

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 的 super.clone 方法中调用 clone 吗?是的,您应该这样做,否则您将无法获得 fruitName 的值,它是 Fruit 中的私有字段。好的,让我们试试这个:

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

然而,它没有用。因为 Fruit#clone 没有调用 super.clone,所以 super.clone() 中的 Apple#clone 的结果返回的是 Fruit 而不是 Apple 的实例。 Fruit 不能转换为 Apple,因此会抛出错误:

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

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

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

答案 2 :(得分:0)

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

如果你想clone使用界面,如果没有选择其他方法名称。

请查看文档:

  

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

您必须致电super.clone()

答案 3 :(得分:0)

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

答案 4 :(得分:0)

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

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

因此,最好遵循API标准,并保持自己不受任何攻击。

答案 5 :(得分:0)

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

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