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