tags:

views:

584

answers:

6

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.

So is there any real reason why enums cannot be cloned or did they forget to think about singletons, when clone() was specified?

P.S.: I ask this question because I am developing within a model-driven environment at which huge parts of the application are generated. And now I have to introduce on more unnecessary case differentiation (as for primitives).

Edit To add one more argument: If the instance itself would be returned, then the singleton pattern would be transparent to referencing objects.

Accepted Answer Most of you just argued with help of the specification, although I asked what were the reasons to specify it that way. That's like a self-fulfilling prophecy. I accepted the answer which points out, that it would be difficult to change what has be around so long. It at least answers my first question about enums.

+1  A: 

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.

Joonas Pulakka
+5  A: 

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.

Dan Dyer
That's exactly what I criticize. Why not return the same instance, if there cannot be a different one?
Christian Strempfer
Because the "contract" for the `clone` method is to return a copy.
Stephen C
@Stephen C: yes, but not every objects must adhere to that contract, ie, not every classes should implement Cloneable.
Miguel Ping
+7  A: 

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...

Miguel Ping
I don't use `clone()` for differentiation. I have to differentiate because there are generated methods which use `clone()` and now I have to change some templates to "clone" enums different than other objects.
Christian Strempfer
@Christian, does your code deal with other objects that don't implement Cloneable, or does everything have to be cloneable?
Dan Dyer
+1  A: 

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.

jprete
A: 

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(); 
}
Pascal Thivent
No, I don't want that. You meant I **should** do it, did you? ;)
Christian Strempfer
Yes, you should. A clone **should** return a clone which doesn't make sense for a singleton.
Pascal Thivent
Why would you want to make `clone` public?
Tom Hawtin - tackline
Maybe because this is the convention when overriding `clone()`, see http://java.sun.com/javase/6/docs/api/java/lang/Cloneable.html.
Pascal Thivent
+2  A: 

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.

gustafc