Why shouldn't Java enum literals be able to have generic type parameters?

Lukas Eder picture Lukas Eder · Nov 27, 2010 · Viewed 27.6k times · Source

Java enums are great. So are generics. Of course we all know the limitations of the latter because of type erasure. But there is one thing I don't understand, Why can't I create an enum like this:

public enum MyEnum<T> {
    LITERAL1<String>,
    LITERAL2<Integer>,
    LITERAL3<Object>;
}

This generic type parameter <T> in turn could then be useful in various places. Imagine a generic type parameter to a method:

public <T> T getValue(MyEnum<T> param);

Or even in the enum class itself:

public T convert(Object o);

More concrete example #1

Since the above example might seem too abstract for some, here's a more real-life example of why I want to do this. In this example I want to use

  • Enums, because then I can enumerate a finite set of property keys
  • Generics, because then I can have method-level type-safety for storing properties
public interface MyProperties {
     public <T> void put(MyEnum<T> key, T value);
     public <T> T get(MyEnum<T> key);
}

More concrete example #2

I have an enumeration of data types:

public interface DataType<T> {}

public enum SQLDataType<T> implements DataType<T> {
    TINYINT<Byte>,
    SMALLINT<Short>,
    INT<Integer>,
    BIGINT<Long>,
    CLOB<String>,
    VARCHAR<String>,
    ...
}

Each enum literal would obviously have additional properties based on the generic type <T>, while at the same time, being an enum (immutable, singleton, enumerable, etc. etc.)

Question:

Did no one think of this? Is this a compiler-related limitation? Considering the fact, that the keyword "enum" is implemented as syntactic sugar, representing generated code to the JVM, I don't understand this limitation.

Who can explain this to me? Before you answer, consider this:

  • I know generic types are erased :-)
  • I know there are workarounds using Class objects. They're workarounds.
  • Generic types result in compiler-generated type casts wherever applicable (e.g. when calling the convert() method
  • The generic type <T> would be on the enum. Hence it is bound by each of the enum's literals. Hence the compiler would know, which type to apply when writing something like String string = LITERAL1.convert(myObject); Integer integer = LITERAL2.convert(myObject);
  • The same applies to the generic type parameter in the T getvalue() method. The compiler can apply type casting when calling String string = someClass.getValue(LITERAL1)

Answer

Lukas Eder picture Lukas Eder · Dec 7, 2016

This is now being discussed as of JEP-301 Enhanced Enums. The example given in the JEP is, which is precisely what I was looking for:

enum Argument<X> { // declares generic enum
   STRING<String>(String.class), 
   INTEGER<Integer>(Integer.class), ... ;

   Class<X> clazz;

   Argument(Class<X> clazz) { this.clazz = clazz; }

   Class<X> getClazz() { return clazz; }
}

Class<String> cs = Argument.STRING.getClazz(); //uses sharper typing of enum constant

Unfortunately, the JEP is still struggling with significant issues: http://mail.openjdk.java.net/pipermail/amber-spec-experts/2017-May/000041.html