改变这个问题为时已晚,但是更精确的是问"为什么clone()不允许单例?"。 copy()方法会更方便。
有什么原因无法克隆Java中的枚举?
该手册指出
This guarantees that enums are never cloned, which is necessary to preserve their"singleton" status.
但是返回实例本身也将保留其状态,并且我将能够以与其他可克隆对象相同的方式处理关联的枚举。
有人可能会争辩说
The general intent [of clone()] is that, for any object x, the expression:
x.clone() != x will be true, [...]
但是相反,对于单身人士,我希望x.clone() == x是真实的。 如果实例本身将被返回,则单例模式对于引用对象将是透明的。
那么为什么在指定clone()时不允许克隆枚举或者忘记了单例和不可变呢?
使用枚举,要克隆的内容是什么?
如果x.clone() == x,则克隆单例的目的是什么?您不能立即使用x吗?
严格来说,如果要克隆某些东西并强制执行x.clone() == x,则唯一可以作为克隆结果的对象就是x本身:
def clone() {
return this;
}
这可能会误导...
如果您正在设计某些东西并且基于clone()进行区分,那么您做错了恕我直言...
@Christian,您的代码是否处理未实现Cloneable的其他对象,还是所有内容都必须是可克隆的?
如果您的克隆方法返回this实例而不是一个不同的对象,则它不是克隆,是吗?
Javadoc说:
By convention, the object returned by
this method should be independent of
this object (which is being cloned).
不应克隆枚举,因为每个值只能有一个实例。
编辑:回应以下评论:
That's exactly what I criticize. Why
not return the same instance, if there
cannot be a different one?
因为这没有任何意义。如果是同一对象,则不是克隆对象。 Javadocs还说:
The general intent is that, for any
object x, the expression:
x.clone() != x
will be true, and that the
expression:
x.clone().getClass() == x.getClass()
will be true, but these
are not absolute requirements.
因此,目的是让clone()方法返回一个不同的对象。不幸的是,它不是绝对要求,这使您的建议有效,但是我仍然认为这不明智,因为具有返回this的克隆方法没有用。如果您做一些可疑的事情,例如在枚举常量中具有可变状态或对其进行同步,甚至可能会导致问题。此类代码的行为会有所不同,具体取决于克隆方法是正确克隆还是仅返回this。
您并没有真正解释为什么当它们本质上是不可克隆的时,为什么要将枚举视为Cloneable。希望拥有一个不遵循公认惯例的克隆方法,似乎可以解决您的方法中的一些更基本的问题。
因为clone方法的"合同"是返回副本。
@Stephen C:是的,但不是每个对象都必须遵守该协定,即,不是每个类都应实现Cloneable。
我认为您引用的Javadoc实际上支持OPs:99%的Java程序员会考虑对象状态的独立性,即修改x的状态不会影响(先前的结果)< x2>。从这个意义上说,用return this实现clone()是非常合理的。我要说的是,依赖于clone()结果的非同一性的任何代码比将不可变单例实例传递给尝试进行clone()的(3rd party?)方法的代码更有可能被破坏。存储对象状态。
But for singletons on the contrary I want x.clone() == x to be true.
您可能想要,但我认为以下代码会中断很奇怪:
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或类似内容的键。
您对问题的自己的回答是最好的。通常,人们希望clone()返回一个不同的对象。 Cloneable本身的语义更有意义。 ("对象是可克隆的……哦,我必须能够复制。")我想不出一个重要的临时情况,但这是Cloneable的预期语义。
我认为即使他们正在考虑单身人士,他们也不会改变它。毕竟,通过有选择地添加(并可能覆盖)Cloneable接口来决定可以克隆什么和不能克隆什么是程序员的责任,而且大多数程序员也不打算将Cloneable接口添加到单例中。
对于不可变对象,实际上与真正实现clone()且仅实现return this的唯一区别是x.clone() != x的结果,您没有真正的理由在乎。如果您遇到的对象可能是可变的或不可变的,则只需进行实际复制即可。不变对象的副本很便宜-您不必递归向下对象图,只需将字段分配为相同即可。
我猜想当指定clone()时,他们不想将单例视为特殊情况。那会使规范变得复杂。因此,现在库开发人员必须将它们视为特例,但对于我们其他人来说,我们可以相信x.clone() != x真是太好了。
But for singletons on the contrary I want x.clone() == x to be true.
不,那不是克隆。因此,对于单身人士,您需要这样做:
是的你应该。克隆应该返回一个对单例没有意义的克隆。
为什么要公开clone?
也许因为这是覆盖clone()时的约定,所以请参见java.sun.com/javase/6/docs/api/java/lang/Cloneable.html。
@ TomHawtin-tackline:如果正在编写一种通用实用程序来尝试克隆对象,则必须知道每个非null字段以标识一个不会以任何会影响字段持有者状态的方式发生变化的对象,否则可以通过某种方式克隆。由于没有其他用于标识不可变类型的标准约定,因此让它们实现clone,因此它返回this可使通用克隆方法更轻松地完成应做的事情。
@ TomHawtin-tackline:此外,在还可能使用可变对象的情况下,经常使用不可变的单例。例如,可以为接受List的代码指定一个可变列表,一个"特殊实例化"不可变列表或一个单例不可变列表(例如一个空列表)。如果对象想要的是列表的私有对象,该对象将始终保持与给定列表相同的内容,则可以在接收到的任何列表上调用clone(无论是否可变),但没有如果列表可变,则创建一个冗余对象-这似乎是最明智的行为。