Why are Java enums not clonable?

2019-04-08 06:42发布

问题:

It's too late to change the question, but more precise would have been to ask "Why does clone() not allow singletons?". A copy() method would be more convenient.


Is there any reason why enums in Java cannot be cloned?

The manual states that

This guarantees that enums are never cloned, which is necessary to preserve their "singleton" status.

But returning the instance itself would also preserve its status, and I would be able to handle associated enums the same way as other clonable objects.

One may argue that

The general intent [of clone()] is that, for any object x, the expression: x.clone() != x will be true, [...]

But for singletons on the contrary I want x.clone() == x to be true. If the instance itself would be returned, then the singleton pattern would be transparent to referencing objects.

So why are enums not allowed to be cloned or did they forget to think about singletons and immutables, when clone() was specified?

回答1:

But for singletons on the contrary I want x.clone() == x to be true.

You may want to, but I think it's weird that the following code would break:

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?

(Of course, since clone() only gives shallow copies, even a correct implementation of it may cause errors in the above code.)

On the other hand, I can sort of agree that it would make sense for cloning operations to just return this for immutable objects, and enums really should be immutable. Now, when the contract for clone() was written, they apparently didn't think about immutables, or they didn't want a special case for a concept that's not supported by the language (i.e., immutable types).

And so, clone() is what it is, and you can't very well go and change something that's been around since Java 1.0. I'm quite certain that somewhere out there, there is code that totally relies on clone() returning a new, distinct object, perhaps as a key for an IdentityHashMap or something.



回答2:

What's the purpose of cloning a singleton, if x.clone() == x? Can't you just use x straight away.

Strictly speaking, if you want to clone something and enforce x.clone() == x, the only object that can be the result of the clone is x itself:

def clone() {
  return this;
}

Which can be misleading...


If you are designing something and are based on clone() for differentiation, you are doing it wrong IMHO...



回答3:

If your clone method returns this instance rather than a distinct object, then it's not a clone, is it?

The Javadoc says:

By convention, the object returned by this method should be independent of this object (which is being cloned).

Enums are not supposed to be cloned because there is supposed to only ever be one instance of each value.

EDIT: In response to the following comment:

That's exactly what I criticize. Why not return the same instance, if there cannot be a different one?

Because it doesn't really make sense. If it's the same object then it's not a clone. The Javadocs also say:

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.

So the intent is for the clone() method to return a distinct object. Unfortunately it says that it's not an absolute requirement, which makes your suggestion valid, but I still think it's not sensible because it's not useful to have a clone method that returns this. It might even cause problems if you were doing something dubious like having mutable state in your enum constants or synchronising on them. The behaviour of such code would be different depending on whether the clone method did proper cloning or just returned this.

You don't really explain why you want to treat enums as Cloneable when they are inherently un-cloneable. Wanting to have a clone method that doesn't obey the accepted conventions seems like a hack to solve some more fundamental problem with your approach.



回答4:

I guess they didn't want to treat singletons as a special case when clone() was specified. That would have complicated the specification. So now the library developers have to treat them as a special case, but for the rest of us, it's nice that we can trust that x.clone() != x.



回答5:

Your own answer to your question is the best one. In general, people expect clone() to give back a different object. The semantics of Cloneable itself make more sense that way. ("The object is cloneable...oh, I must be able to make copies.") I can't think of a situation offhand where that matters, but that was the intended semantic meaning of Cloneable.

I think that even if they were thinking about singletons, they would not have changed it. After all, it's the programmer's responsibility to decide what can be cloned and what can't, by selectively adding (and potentially overriding) the Cloneable interface, and most programmers are not going to add the Cloneable interface to singletons either.



回答6:

But for singletons on the contrary I want x.clone() == x to be true.

No, that wouldn't be a clone. So, for singletons, you want this:

public Object clone() throws CloneNotSupportedException {
  throw new CloneNotSupportedException(); 
}


标签: java enums clone