Skip to content
Advertisement

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

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)

Advertisement

Answer

This has been discussed as of JEP-301 Enhanced Enums, which was withdrawn, regrettably. 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 was struggling with significant issues, which couldn’t be resolved: http://mail.openjdk.java.net/pipermail/amber-spec-experts/2017-May/000041.html

Advertisement