Cloneable
本质上是破碎的。具体来说,我对界面的最大问题是它需要一种不定义方法本身的方法行为。因此,如果遍历Cloneable
列表,则必须使用反射来访问其定义的行为。但是,在Java 8中,我们现在有默认方法,现在我问为什么clone()
中没有默认的Cloneable
方法。
我理解为什么interfaces cannot default Object methods,但这是一个明确的设计决定,因此可以做出例外。
我想要弃用Object.clone()
并将其内部代码更改为:
if(this instanceof Cloneable) {
return ((Cloneable) this).clone();
}
else {
throw new CloneNotSupportedException();
}
继续使用任何魔法使clone()
作为Cloneable
中的默认方法。这并没有真正解决clone()
仍然可以轻易实现错误的问题,但这本身就是另一种讨论。
据我所知,这种改变完全可以向后兼容:
clone()
但未实现Cloneable
(为什么?!)的类在技术上仍然可行(即使在功能上不可行,但这与以前没有什么不同)。< / LI>
clone()
但已实施Cloneable
的类在其实现方面的功能仍然相同。clone()
但已实现Cloneable
(为什么?!)的类现在将遵循规范,即使它完全功能正确。 Object.clone()
的人仍然可以在功能上工作。super.clone()
在功能上仍然相同,即使它引用Object.clone()
。更不用说这将解决Cloneable
的一个巨大问题。虽然繁琐且仍然容易错误地实现,但它将解决面向对象的巨大问题。
我能看到的唯一问题是,实施Cloneable
的人没有义务覆盖clone()
,但这与以前没有什么不同。
这是否在内部进行了讨论,但从未实现过?如果是这样,为什么?如果是因为接口不能默认使用Object方法,那么在这种情况下做出异常是否有意义,因为继承Cloneable
的所有对象无论如何都期待clone()
?
答案 0 :(得分:8)
你的问题有点广泛而且更多的讨论,但我可以对这个问题有所了解。
在 Effective Java™中,Joshua Bloch给出了相关情况的简要说明。他打开了Cloneable
Cloneable接口旨在作为对象的mixin接口 宣传他们允许克隆。不幸的是,它没有达到这个目的。它的主要缺陷是缺少克隆方法,而Object的克隆方法受到保护。如果不依靠反射,就不能仅仅因为它实现Cloneable而在对象上调用clone方法。
继续推理
[Cloneable]确定Object的受保护克隆实现的行为:如果一个类实现Cloneable,则Object的clone方法返回该对象的逐个字段副本...这是一个非常非典型的接口使用,而不是一个被模仿。通常,实现接口会说明类可以为其客户做些什么。在Cloneable的情况下,它修改了超类上受保护方法的行为。
和
如果实现Cloneable接口对类有任何影响,那么 class及其所有超类必须服从相当复杂,不可执行的,并且 细致记录的协议。结果机制是extralinguistic:它创建一个对象而不调用构造函数。
这里有很多细节,但要注意一个问题:
克隆体系结构与引用可变对象的最终字段的正常使用不兼容。
我认为这足以说明在接口中使用default
方法进行克隆。正确实施它会非常复杂。
答案 1 :(得分:6)
我的经验可能远不是主流,但我使用clone()
并支持Cloneable
的当前设计。可能最好将其作为注释,但Cloneable
早在注释之前出现。我的观点是Cloneable
是一个低级别的东西,没有人应该做obj instanceof Cloneable
之类的事情。如果在某些业务逻辑中使用Cloneable
,那么声明自己的接口或抽象类会更好地公开clone()
并在所有业务逻辑对象中实现它。有时您可能不希望实际公开clone()
,而是创建自己的内部使用clone()
的方法。
例如,假设您有一个命名对象的层次结构,其中名称在构造后无法更改,但您希望允许使用新名称克隆它们。您可以创建一些这样的抽象类:
public abstract class NamedObject implements Cloneable {
private String name;
protected NamedObject(String name) {
this.name = name;
}
public final String getName() {
return name;
}
public NamedObject clone(String newName) {
try {
NamedObject clone = (NamedObject)super.clone();
clone.name = newName;
return clone;
}
catch(CloneNotSupportedException ex) {
throw new AssertionError();
}
}
}
即使您实施Cloneable
,也要使用clone()
,但不想公开公开它。相反,您提供了另一种允许使用其他名称进行克隆的方法。因此,在clone()
中使用公共Cloneable
会不必要地污染您的类的公共接口。
我使用Cloneable
的另一个案例是Spliterator.trySplit()
的实现。请参阅简单spliterator的implementation,它返回给定数量的常量对象。它有四个特化(对象,整数,长整数和双精度),但是由于clone()
,我只能在超类中实现trySplit()
一次。同样,我不想公开clone()
,我只想自己使用它。
总而言之,在clone()
界面中没有Cloneable
方法实际上更灵活,因为它允许我决定是否要公开它。