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