tags:

views:

272

answers:

2

Possible Duplicate:
java Enum definition

if language designers were to use simply Enum<E extends Enum> how would that affect the language?

The only difference now would be that someone coud write

A extends Enum<B>

but since it is not allowed in java to extend enums that would be still illegal. I was also thinking about someone supplying jvm a bytecode that defines smth as extending an enum - but generics can't affect that as they all are erased.

So what is the whole point of such declaration?

Thank you!

Edit for simplicity let's look at an example:

interface MyComparable<T> {
    int myCompare(T o);
}

class MyEnum<E extends MyEnum> implements MyComparable<E> {
    public int myCompare(E o) { return -1; }
}

class FirstEnum extends MyEnum<FirstEnum> {}

class SecondEnum extends MyEnum<SecondEnum> {}

what's wrong with this class structure? What can be done that "MyEnum<E extends MyEnum<E>>" would restrict?

+5  A: 

This is a common question, and understandably so. Have a look at this part of the generics FAQ for the answer (and actually, read as much of the whole document as you feel comfortable with, it's rather well done and informative).

The short answer is that it forces the class to be parameterized on itself; this is required for superclasses to define methods, using the generic parameter, that work transparently ("natively", if you will) with their subclasses.

Edit: As a (non-)example for instance, consider the clone() method on Object. Currently, it's defined to return a value of type Object. Thanks to covariant return types, specific subclasses can (and often do) define that they return a more specific class, but this cannot be enforced and hence cannot be inferred for an arbitrary class.

Now, if Object were defined like Enum, i.e. Object<T extends Object<T>> then you'd have to define all classes as something like public class MyFoo<MyFoo>. Consequently, clone() could be declared to return a type of T and you can ensure at compile time that the returned value is always exactly the same class as the object itself (not even subclasses would match the parameters).

Now in this case, Object isn't parameterized like this because it would be extremely annoying to have this baggage on all classes when 99% of them aren't going to utilise it at all. But for some class hierarchies it can be very useful - I've used a similar technique myself before with types of abstract, recursive expression parsers with several implementations. This construct made it possible to write code that was "obvious" without having to cast everywhere, or copy-and-paste just to change concrete class definitions.

Edit 2 (To actually answer your question!):

If Enum was defined as Enum<E extends Enum>, then as you rightly say, someone could define a class as A extends Enum<B>. This defeats the point of the generic construct, which is to ensure that the generic parameter is always the exact type of the class in question. Giving a concrete example, Enum declares its compareTo method as

public final int compareTo(E o)

In this case, since you defined A to extend Enum<B>, instances of A could only be compared against instances of B (whatever B is), which is almost certainly not very useful. With the additional construct, you know that any class that extends Enum is comparable only against itself. And hence you can provide method implementations in the superclass that remain useful, and specific, in all subclasses.

(Without this recursive generics trick, the only other option would be to define compareTo as public final int compareTo(Enum o). This is not really the same thing, as then one could compare a java.math.RoundingMode against a java.lang.Thread.State without the compiler complaining, which again isn't very useful.)


OK, let's get away from Enum itself as we appear to be getting hung up on it. Instead, here is an abstract class:

public abstract class Manipulator<T extends Manipulator<T>>
{
    /**
     * This method actually does the work, whatever that is
     */
    public abstract void manipulate(DomainObject o);

    /**
     * This creates a child that can be used for divide and conquer-y stuff
     */
    public T createChild()
    {
        // Some really useful implementation here based on
        // state contained in this class
    }
}

We are going to have several concrete implementations of this - SaveToDatabaseManipulator, SpellCheckingManipulator, whatever. Additionally we also want to let people define their own, as this is a super-useful class. ;-)

Now - you will notice that we're using the recursive generic definition, and then returning T from the createChild method. This means that:

1) We know and the compiler knows that if I call:

SpellCheckingManipulator obj = ...; // We have a reference somehow
return obj.createChild();

then the returned value is definitely a SpellCheckingManipulator, even though it's using the definition from the superclass. The recursive generics here allow the compiler to know what is obvious to us, so you don't have to keep casting the return values (like you often have to do with clone(), for example).

2) Notice that I didn't declare the method final, since perhaps some specific subclasses will want to override it with a more suitable version for themselves. The generics definition means that regardless of who create a new class or how it is defined, we can still assert that the return from e.g. BrandNewSloppilyCodedManipulator.createChild() will still be an instance of BrandNewSloppilyCodedManipulator. If a careless developer tries to define it to return just Manipulator, the compiler won't let them. And if they try to define the class as BrandNewSloppilyCodedManipulator<SpellCheckingManipulator>, it won't let them either.

Basically, the conclusion is that this trick is useful when you want to provide some functionality in a superclass that somehow gets more specific in subclasses. By declaring the superclass like this, you are locking down the generic parameter for any subclasses to be the subclass itself. This is why you can write a generic compareTo or createChild method in the superclass and prevent it from becoming overly vague when you're dealing with specific subclasses.

Andrzej Doyle
i've read that FAQ (should have mentioned that in my qn probably), but it doesn't answer *my* question!"this is required for superclasses to define methods, using the generic parameter" <-- could you provide an example of such methods that won't work if i replace it with "Enum<E extends Enum>"?
atamur
Wrt to your example: that can be achieved by defining Object as Object<T extends Object> as far as I can see. No need in additional recursion - that's why I'm asking the question!
atamur
thanks for your time, but as for your second edit: I stated in my qn that this situation is not possible because nothing except javac can extend an enum and by the time javac actually extends an enum all the generics are erased already, so this doesn't look like a real reason.
atamur
and to reiterate: w/o the recursive trick you can still define compareTo(E o) which serves the task perfectly well!
atamur
Firstly - Enum isn't magic. You can't extend it directly, but once created it's still a normal class and the generics definitions still have to match up (I'm assuming here that the code generation step happens before passing everything to `javac` as usual. Perhaps it *could* be magic. :-)). Additionally, as I mentioned `Enum` isn't the only class that does this/can do this. I've used this before on my own classes to good effect.
Andrzej Doyle
If you define `compareTo(E o)` *without* the recursive trick, then subclasses can break the semantics of the method. In your specific example, you now have a class `A` that can't be compared against itself! To reiterate, the point of the recursive trick is so that you **know** your generic parameter will be *exactly* the type of the class (subclasses included). If you don't need this fact, you don't need to use the trick, but if you do want this constraint, it's the only way to apply it.
Andrzej Doyle
"then subclasses can break the semantics of the method" but they can't according to jls that doesn't allow extending the enum! So the only reason for this was that language designers didn't trust javac/jls? That's doesn't sound reasonable.
atamur
"you now have a class A that can't be compared against itself!" - could you pls elaborate an example?
atamur
thanks for the example! if you check it with javac you could notice that sometimes what you think of it is not true. I.e. it won;t stop you from defining `BrandNewSloppilyCodedManipulator<SpellCheckingManipulator>`. Then `createChild` works fine w/o the recursive bit - try it! - it still doesn't allow you to return Manipulator instead of a subclass. So I'm afraid I still don't see how it is useful for enums. I've created a new question with a bit more strict wording so that it's not closed as a duplicate - would be happy to see you there.
atamur
+1  A: 

The only difference now would be that someone coud write

A extends Enum<B>

but since it is not allowed in java to extend enums that would be still illegal. I was also thinking about someone supplying jvm a bytecode that defines smth as extending an enum - but generics can't affect that as they all are erased.

You're right that the language would prevent someone from doing that. However, the benefit of making it E extends Enum<E> rather than just extends Enum is that some of the methods on Enum will now return the correct, specific type.

The poster asked for something that wouldn't work. If Enum was defined as Enum<E>, then you could do this:

// Would be legal, because element types of `DaysOfWeek` and `Color` both
//   extend `Enum<E>`, not `Enum<E extends Enum<E>>`.
DaysOfWeek d = Enum.valueOf(Color.class, "Purple");
John Feminella
Sorry, I still don't see how making it "E extends Enum" would make "values()" return Enum instead of E.
atamur
"Notice the elements of values are instances of your enumeration" <- you can achieve that by simply using Enum<E extends Enum> and then CardSuit extends Enum<CardSuit>. I've tried that in my IDE/javac - it works. Could you post an example code that won't work?
atamur
wrt your latest edit: this way "public static <T extends MyEnum> T valueOf(Class<T> enumType, String name)" (see my example in the qn body) it doesn't work either (i.e. compiler rejects this code) - still can't see a need for that recursion.
atamur