views:

176

answers:

5

Please see http://stackoverflow.com/questions/211143/java-enum-definition and http://stackoverflow.com/questions/3061759/why-in-java-enum-is-declared-as-enume-extends-enume for general discussion. Here I would like to learn what exactly would be broken (not typesafe anymore, or requiring additional casts etc) if Enum class was defined as

public class Enum<E extends Enum> 

I'm using this code for testing my ideas:

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> {}

With it I wasn't able to find any benefits in this exact case.

PS. the fact that I'm not allowed to do

class ThirdEnum extends MyEnum<SecondEnum> {}

when MyEnum is defined with recursion is
a) not relevant, because with real enums you are not allowed to do that just because you can't extend enum yourself
b) not true - pls try it in a compiler and see that it in fact is able to compile w/o any errors

PPS. I'm more and more inclined to believe that the correct answer here would be "nothing would change if you remove the recursive part" - but I just can't believe that.

A: 

Consider Enum<E>.compareTo(E other).

That:

  • Needs to work with E rather than enum so that you don't try to compare one enum value with a value from a different enum
  • Needs to be able to get the ordinal value of the enum, via the ordinal() method declared on Enum.

How would you propose making that work without the current constraint?

That's just the first one I came up with... I'm sure there are plenty of others. Basically it's a way of saying, "You shouldn't try to treat all enums as equivalent to each other... an enum is a closed set in itself, but all enums share certain properties."

Jon Skeet
compareTo - see my qn. In that way it works exactly as you say. W/o any recursion. Don't see a problem with ordinal either - could you provide some code that breaks/is not typesafe?
atamur
@atamur: I hadn't noticed the raw type bit in your example (did you edit within the first five minutes of asking? Maybe I just misread it). The fact that you need a raw type immediate makes me nervous, to be honest...
Jon Skeet
+2  A: 

Well, first of all it would complain about using the raw type, but you could do:

public class Enum<E extends Enum<?>>

for the same effect.

Also, with this type of generic you could do something like:

class FirstEnum extends MyEnum<SecondEnum> {
}

class SecondEnum extends MyEnum<FirstEnum> {
}

which to me seems like it could lead to a lot of trouble. More exactly you can't compare an enum of type FirstEnum with an enum of the same type, you have to compare it with an enum of the other type, which is really troublesome if you have a List<FirstEnum> that you want sorted. The example will not compile if i set E instead o ? since SecondEnum is not of the type E extends MyEnum<E> (this would lead to circular inheritance). It will work if FirstEnum extends MyEnum<FirstEnum> though (which would mean that SecondEnum is a child class of FirstEnum - normal hierarchical inheritance).

Andrei Fierbinteanu
The raw type sounds interesting - but javac doesn't emit any raw warnings if you actually try that.pls see my ps in the qn. There are 2 points why this is not relevant.
atamur
I'm using eclipse and it gives warning about raw type (not to mention that sun have said that we shouldn't use raw types anymore). Maybe javac requires some sort of option activated to complain about this?
Andrei Fierbinteanu
Also, the second point of the ps is not true for my example, if I set the class to `public class MyEnum<E extends MyEnum<E>>` it doesn't compile. As for the first point, I think it might be able to generate the .class file so that it has the method signatures like the classes were written like this (by using a non-standard compiler) even though javac would not let you compile `public enum FirstEnum extends MyEnum<SecondEnum>` or something similar.
Andrei Fierbinteanu
see addition to the last paragraph as well.
Andrei Fierbinteanu
as for raw type - I will check out the jls (there should be a reason why javac is silent), I'm not completely convinced that in this case it is still considered as a raw type usage.
atamur
can u post a code that is not compiling? For me this code is compiled by javac fine: http://pastebin.com/0sCYcYsT
atamur
back to "bad compiler" that could produce bad bytecode - in this case generics can't help you, cuz they are erased from bytecode.
atamur
The code above where FirstEnum has type param to SecondEnum and SecondEnum has type param set to FirstEnum (they have each other as a type param - ciclical) does not compile if you set E instead of ? for MyEnum. AS for the bytecode, i think you could create a class `FirstEnum extends MyEnum implements Comparable` (no generic) that has the method `public int compareTo(SecondEnum other)` which would match the declaration `FirstEnum extends MyEnum<SecondEnum>` (i'm not exactly sure how that would be done though, haven't really played around with bytecode, but I think it can be done).
Andrei Fierbinteanu
A: 

If you didn't have the generic type parameter, then you wouldn't be able to extend Comparable<T extends Comparable> for the specific subtype of enum you have created.

You could ignore this, and create your own MyEnum type which behaves much the same but without the restriction that different MyEnum are not comparable:

public abstract class MyEnum implements Comparable<MyEnum>
{
    private final int ordinal;

    protected MyEnum ( int ordinal )
    {
        this.ordinal = ordinal;
    }

    public int ordinal ()
    {
        return ordinal ;
    }

    public int compareTo ( MyEnum other )
    {
        return ordinal - other.ordinal; // ignore overflow for now
    }

    public boolean equals (Object other) {
        return ordinal == ((MyEnum)other).ordinal;
    }

    public int hashCode () {
        return ordinal;
    }
}

This behaves in much the same way as an enum would for the operations defined, but instead of being a generic type safe implementation obeys LSP - objects of different subclasses of MyEnum are comparable or equal to each other if they have the same ordinal value.

public static class EnumA extends MyEnum
{
    private EnumA ( int ordinal ) { super ( ordinal ); }
    public static final EnumA a = new EnumA ( 0 );
    public static final EnumA b = new EnumA ( 1 );
}

public static class EnumB extends MyEnum
{
    private EnumB ( int ordinal ) { super ( ordinal ); }
    public static final EnumB x = new EnumB ( 0 );
    public static final EnumB y = new EnumB ( 1 );
}

public static void main ( String...args )
{
    System.out.println ( EnumA.a.compareTo ( EnumB.x ) );
    System.out.println ( EnumA.a.equals ( EnumB.x ) );
    System.out.println ( EnumA.a.compareTo ( EnumB.y ) );
    System.out.println ( EnumA.a.equals ( EnumB.y ) );
}

In this case, if you don't override equals, you lose the convention that x.comparesTo(y)=0 implies x.equals(y); if you do override equals then there are cases where x.equals(y) does not imply x == y (as for other value objects), whereas for Java enums both tests of equality produce the same result.

Pete Kirkham
I'm afraid you didn't understand my qn. If you look at my code - MyEnum is using generics, just not the recursive bit.
atamur
+1  A: 

I believe that a compelling reason to do that is that it makes the code in the MyEnum class more typesafe.

Consider that the recursive part makes such a thing possible:

class MyEnum<E extends MyEnum<E>> {
    private Thing<E> util1() { return someObject }
    private void util2(E e) {}
    public int method(E o) { 
        Thing<E> thingy = o.util1(); 
        // You can call the util1 method on o and get a type safe return element.
        E o1 = // I don't care how you get a parametrized E object.
        o.util2(o1);
        // You can call the util2 method with a typesafe parameter.
    }
}

In short, that recursivity lets you put typesafe methods in the Enum class that you can call on any E element, and these calls will be typesafe.

jhominal
just a small note, `o.util2(this)` will not compile since this is considered MyEnum<E> not E. You could add a second param `E o1` and call `o.util2(o1)` though for the same effect: it will compile with `E extends MyEnum<E>`, but not with `E extends MyEnum<?>`
Andrei Fierbinteanu
Thank you, I just corrected that mistake.
jhominal
thank you! that code indeed is marked as unsafe if you remove the recursion! Do you know any real-life usage of that feature in jdk?
atamur
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/Enum.java The Enum class itself uses that method for the `getDeclaringClass` method (which is in turn used in the `compareTo` method).
jhominal
A: 
`X extends Enum<Y>`

would be illegal. that is relavant. compiler can have special rules with enum, but why not have a perfect type declaration if possible?

irreputable