Why are Java enums not clonable?

13,717

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

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

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

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

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

Share:
13,717
Christian Strempfer
Author by

Christian Strempfer

Interesting stuff: Mylar for encrypted cloud services. Doing full text-search and calculations on encrypted data. Chaos Monkey

Updated on June 09, 2022

Comments

  • Christian Strempfer
    Christian Strempfer over 1 year

    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?

  • Devanshu Mevada
    Devanshu Mevada almost 14 years
    Yes, you should. A clone should return a clone which doesn't make sense for a singleton.
  • Stephen C
    Stephen C almost 14 years
    Because the "contract" for the clone method is to return a copy.
  • Dan Dyer
    Dan Dyer almost 14 years
    @Christian, does your code deal with other objects that don't implement Cloneable, or does everything have to be cloneable?
  • Miguel Ping
    Miguel Ping almost 14 years
    @Stephen C: yes, but not every objects must adhere to that contract, ie, not every classes should implement Cloneable.
  • Brett
    Brett almost 14 years
    Why would you want to make clone public?
  • Devanshu Mevada
    Devanshu Mevada almost 14 years
    Maybe because this is the convention when overriding clone(), see java.sun.com/javase/6/docs/api/java/lang/Cloneable.html.
  • misberner
    misberner about 10 years
    I think the Javadoc you quoted does actually support OPs point: 99% of all Java programmers will think of independence in terms of object state, i.e., modifying the state of x won't affect the state of (the result of a previous) x.clone(). And in this sense it is very reasonable to implement clone() by return this. I would go as far as to say that any code relying on non-identity of clone() result is much more likely to be broken than code which passes immutable singleton instances to (3rd party?) methods that attempt to clone(), e.g. to store the object state.
  • supercat
    supercat almost 10 years
    @TomHawtin-tackline: If one is writing a general-purpose utility to try to clone an object, every non-null field must either be known to identify an object that will never change in any way that would affect the field-holder's state, or else be cloneable via some means. Since there's no other standard convention for identifying immutable types, having them implement clone so it returns this makes it easier for a general-purpose cloning method to do what should be done.
  • supercat
    supercat almost 10 years
    @TomHawtin-tackline: Further, immutable singletons are often used in cases where mutable objects might also be used. For example, code which accepts a List<String> might be given a mutable list, a "specially-instantiated" immutable list, or a singleton immutable list (e.g. an empty one). If what an object wants is a private object to a list which will always hold the same contents as a list which it was given, being able to call clone on any list it receives--mutable or not--but not have it create a redundant object if the list was mutable--would seem the most sensible behavior.
  • Kevin
    Kevin about 8 years
    For immutable objects, literally the only difference from actually implementing clone() correctly and just return this is the result of x.clone() != x, which you have no real reason to care about. If you're in a situation where you might have a mutable or immutable object, just make an actual copy. Copies of immutable objects are cheap - you don't have to recurse down the object graph, just assign the fields to be the same.