为什么Java枚举文字不能有genericstypes参数?

Java枚举是伟大的。 generics也是如此。 当然,我们都知道后者由于types擦除的局限性。 但有一件事我不明白,为什么我不能创build一个像这样的枚举:

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

这个genericstypes参数<T>反过来可以在各个地方有用。 设想一个方法的genericstypes参数:

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

甚至在枚举类本身:

 public T convert(Object o); 

更具体的例子#1

由于上面的例子对于某些人来说似乎太抽象了,下面是为什么我想要这样做的一个更现实的例子。 在这个例子中,我想使用

  • 枚举,因为那么我可以列举一组有限的属性键
  • generics,因为那样我可以有方法级别的types安全性来存储属性
 public interface MyProperties { public <T> void put(MyEnum<T> key, T value); public <T> T get(MyEnum<T> key); } 

更具体的例子#2

我有一个数据types的枚举:

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

每个枚举字面值显然会有基于genericstypes<T>附加属性,同时也是一个枚举(不可变,单例,枚举等)

题:

没有人想到这个? 这是一个编译器相关的限制吗? 考虑到这个事实,关键字“ enum ”被实现为语法糖,代表生成的代码给JVM,我不理解这个限制。

谁能解释给我? 在你回答之前,考虑一下:

  • 我知道genericstypes被删除:-)
  • 我知道有使用Class对象的解决方法。 他们是解决方法。
  • genericstypes导致编译器生成的types强制转换(例如,在调用convert()方法时)
  • genericstypes<T>将在枚举上。 因此它受到每个枚举文字的约束。 因此,编译器会知道在编写类似String string = LITERAL1.convert(myObject); Integer integer = LITERAL2.convert(myObject);时应用的typesString string = LITERAL1.convert(myObject); Integer integer = LITERAL2.convert(myObject); String string = LITERAL1.convert(myObject); Integer integer = LITERAL2.convert(myObject);
  • T getvalue()方法中的genericstypes参数也是T getvalue() 。 编译器可以在调用String string = someClass.getValue(LITERAL1)时应用types转换String string = someClass.getValue(LITERAL1)

现在正在讨论JEP-301 Enhanced Enums 。 JEP中给出的例子正是我所期待的:

 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 

答案在于:

由于types擦除

这两种方法都不可能,因为参数types被擦除。

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

为了实现这些方法,你可以构造你的枚举为:

 public enum MyEnum { LITERAL1(String.class), LITERAL2(Integer.class), LITERAL3(Object.class); private Class<?> clazz; private MyEnum(Class<?> clazz) { this.clazz = clazz; } ... } 

因为你不能。 认真。 这可以添加到语言规范。 它没有。 这会增加一些复杂性。 这对成本的好处意味着它不是一个高度优先事项。

更新:当前正在添加到JEP 301:增强的枚举下的语言。

ENUM中还有其他的方法不起作用。 什么是MyEnum.values()返回?

那么MyEnum.valueOf(String name)呢?

对于valueOf,如果你认为编译器可以使类似的方法

public static MyEnum valueOf(String name);

为了像MyEnum<String> myStringEnum = MyEnum.value("some string property")调用它,这也不会工作。 例如,如果你调用MyEnum<Int> myIntEnum = MyEnum.<Int>value("some string property") ? 这是不可能实现该方法正常工作,例如抛出exception或返回null,当你调用它像MyEnum.<Int>value("some double property")因为types擦除。

坦率地说,这看起来更像是寻找问题的解决scheme。

Java枚举的全部目的是模拟一个types实例的枚举,这些types实例共享相似的属性,以提供超出可比较的string或整数表示的一致性和丰富性。

举一个教科书枚举的例子。 这不是很有用或一致:

 public enum Planet<T>{ Earth<Planet>, Venus<String>, Mars<Long> ...etc. } 

我为什么要让不同的星球有不同的generics转换? 它解决了什么问题? 这是否有理由使语言语义复杂化? 如果我确实需要这种行为是一个枚举实现它的最佳工具?

另外,您将如何pipe理复杂的转换?

例如

 public enum BadIdea<T>{ INSTANCE1<Long>, INSTANCE2<MyComplexClass>; } 

使用String Integer来提供名称或序号很容易。 但generics将允许您提供任何types。 你将如何pipe理到MyComplexClass的转换? 现在,通过迫使编译器知道可以提供给generics枚举的types的有限子集,并为概念(generics)引入额外的混淆,已经使很多程序员避而远之。

我认为,因为基本上枚举不能被实例化

如果JVM允许你这样做,你将在哪里设置T类?

枚举是应该总是相同的数据,或者至less是不会改变的。

新的MyEnum <>()?

还是下面的方法可能是有用的

 public enum MyEnum{ LITERAL1("s"), LITERAL2("a"), LITERAL3(2); private Object o; private MyEnum(Object o) { this.o = o; } public Object getO() { return o; } public void setO(Object o) { this.o = o; } } 

因为“枚举”是枚举的缩写。 这只是一组指定的常量,代替序数来使代码更易读。

我没有看到types参数化常量的意图是什么。