在不实现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
,它就像复制构造函数一样。
这是一个很好的方法吗?
答案 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
之类的东西)和添加新字段时的持久性错误。