为什么Java枚举不可克隆?
改变问题为时已晚,但更为精确的是问“为什么克隆()不允许单身人士?”。 copy()
方法会更方便。
是否有任何理由不能克隆Java中的枚举?
手册说明了这一点
这保证了枚举永远不会被克隆,这是保持其“单身”状态所必需的。
但是返回实例本身也会保留其状态,并且我能够以与其他可克隆对象相同的方式处理关联的枚举。
有人可能会说
[clone]的一般意图是,对于任何对象x,表达式:
x.clone() != x
将为true,[…]
但对于单身人士来说,我希望x.clone() == x
为真。 如果返回实例本身,则单例模式对引用对象是透明的。
那么,当指定了clone()
时,为什么不允许克隆枚举或者忘记考虑单例和不可变因素?
但对于单身人士来说,我希望
x.clone() == x
为真。
你可能想要,但我认为下面的代码会破坏是很奇怪的:
interface Foo extends Cloneable { public int getX(); public void setX(int x); } enum FooSingleton implements Foo { INSTANCE; private int x; public int getX(){ return x; } public void setX(int x){ this.x = x; } } class FooClass implements Foo { private int x; public int getX(){ return x; } public void setX(int x){ this.x = x; } } boolean omg(Foo f){ Foo c = f.clone(); c.setX(c.getX() + 1); return c.getX() != f.getX(); } assert omg(new FooClass()); // OK assert omg(FooSingleton.INSTANCE); // WTF?
(当然,由于clone()
只提供浅拷贝,即使正确实现它也可能导致上述代码出错。)
另一方面,我可以同意克隆操作只是为不可变对象return this
它是有意义的,并且枚举确实应该是不可变的。 现在,当编写了clone()
的契约时,他们显然没有考虑不可变,或者他们不想要一个特殊情况用于语言不支持的概念(即不可变类型)。
因此, clone()
就是这样,你不可能去改变自Java 1.0以来一直存在的东西。 我很确定在那里的某个地方,有一些代码完全依赖于clone()
返回一个新的,不同的对象,可能作为IdentityHashMap
一个关键点。
如果x.clone() == x
,克隆单例的目的是什么? 你不能直接使用x
。
严格地说,如果你想克隆一些东西并强制执行x.clone() == x
,那么唯一可能是克隆结果的对象就是x
本身:
def clone() { return this; }
这可能会误导……
如果你正在设计一些东西并且基于clone()
进行区分,那么你做错了恕我直言……
如果你的clone方法返回this
实例而不是一个不同的对象,那么它不是克隆,是吗?
Javadoc说:
按照惯例,此方法返回的对象应独立于此对象(正在克隆)。
枚举不应该被克隆,因为应该只有每个值的一个实例。
编辑:回应以下评论:
这正是我所批评的。 为什么不返回相同的实例,如果不能有不同的实例?
因为它没有意义。 如果它是同一个对象,那么它不是克隆。 Javadocs还说:
一般意图是,对于任何对象x,表达式:
x.clone()!= x将是真的,那表达式:
x.clone()。getClass()== x.getClass()将是真的,但这些并非绝对要求。
因此,目的是让clone()
方法返回一个不同的对象。 不幸的是,它说这不是一个绝对的要求,这使你的建议有效,但我仍然认为这是不明智的,因为有一个克隆方法返回this
没用。 如果你做了一些可疑的事情,比如你的枚举常量中有可变状态或同步它们,它甚至可能会引起问题。 这些代码的行为会有所不同,具体取决于克隆方法是否进行了适当的克隆,或者只是返回了this
。
您没有真正解释为什么要将枚举视为Cloneable
因为它们本身就是不可复制的。 想要一个不遵守公认惯例的克隆方法似乎是解决一些更基本问题的方法。
我猜他们不想在指定clone()
时将单身人士视为一种特殊情况。 这会使规范变得复杂。 因此,现在库开发人员必须将它们视为特殊情况,但对于我们其他人来说,我们可以信任x.clone() != x
很好。
你自己对问题的回答是最好的。 一般来说,人们希望clone()
能够回馈不同的对象。 Cloneable
本身的语义更有意义。 (“这个对象是可复制的……哦,我必须能够制作副本。”)我无法想到一个重要的情况,但那是Cloneable
的预期语义。
我认为即使他们考虑单身人士,他们也不会改变它。 毕竟,程序员有责任通过有选择地添加(并可能覆盖) Cloneable
接口来决定可克隆的内容和Cloneable
,并且大多数程序员也不会将Cloneable
接口添加到单例。
但对于单身人士来说,我希望
x.clone() == x
为真。
不,那不会是克隆。 所以,对于单身人士,你想要这个:
public Object clone() throws CloneNotSupportedException { throw new CloneNotSupportedException(); }