为什么Java枚举文字不能具有泛型类型参数?

时间:2010-11-27 09:12:05

标签: java generics enums

Java枚举很棒。仿制药也是如此。当然,由于类型擦除,我们都知道后者的局限性。但有一点我不明白,为什么我不能创建这样的枚举:

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

这个泛型类型参数<T>反过来可以在各个地方使用。想象一下方法的泛型类型参数:

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

甚至在枚举类本身中:

public T convert(Object o);

更具体的例子#1

由于上面的例子对某些人来说可能看起来过于抽象,所以这里有一个更真实的例子,说明我为什么要这样做。在这个例子中,我想使用

  • 枚举,因为那时我可以枚举一组有限的属性键
  • 泛型,因为那时我可以使用方法级类型安全来存储属性
public interface MyProperties {
     public <T> void put(MyEnum<T> key, T value);
     public <T> T get(MyEnum<T> key);
}

更具体的例子#2

我有一个数据类型的枚举:

public interface DataType<T> {}

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

每个枚举文字显然都有基于泛型类型<T>的附加属性,同时又是枚举(不可变,单例,可枚举等等)

问题:

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

谁可以向我解释这个?在你回答之前,请考虑一下:

  • 我知道泛型类型被删除: - )
  • 我知道有使用Class对象的变通方法。他们是解决方法。
  • 通用类型会在适用的情况下导致编译器生成的类型转换(例如,在调用convert()方法时
  • 通用类型&lt; T&gt;将是枚举。因此它受每个枚举文字的约束。因此编译器会知道在编写String string = LITERAL1.convert(myObject); Integer integer = LITERAL2.convert(myObject);
  • 之类的内容时应用哪种类型
  • 这同样适用于T getvalue()方法中的泛型类型参数。编译器可以在调用String string = someClass.getValue(LITERAL1)
  • 时应用类型转换

8 个答案:

答案 0 :(得分:46)

现在正在讨论FactoryGirl。 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

不幸的是,JEP仍在努力应对重大问题:JEP-301 Enhanced Enums

答案 1 :(得分:11)

答案在于问题:

  

因为类型擦除

这两种方法都不可能,因为参数类型被删除。

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

    ...

}

答案 2 :(得分:4)

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

更新:目前正在添加到JEP 301: Enhanced Enums下的语言。

答案 3 :(得分:4)

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")怎么办? 无法实现该方法以正常工作,例如抛出异常或在因为类型擦除而调用MyEnum.<Int>value("some double property")时返回null。

答案 4 :(得分:1)

坦率地说,这似乎是解决问题的更多解决方案。

java enum的全部目的是为类型实例的枚举建模,这些类型实例共享相似的属性,提供超出可比较的String或Integer表示的一致性和丰富性。

以教科书enum为例。这不是非常有用或一致:

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

为什么我希望不同的行星具有不同的泛型类型转换?它解决了什么问题?它是否有理由使语言语义复杂化?如果我确实需要这种行为,那么enum是实现它的最佳工具吗?

此外,您将如何管理复杂的转化?

for Instance

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

使用String Integer很容易提供名称或序号。但是泛型可以让你提供任何类型。您如何管理转换为MyComplexClass?现在你通过强迫编译器知道可以提供给通用枚举的类型的有限子集并引入对概念(泛型)的额外混淆来解决两个构造,这些概念已经躲过了很多程序员。

答案 5 :(得分:0)

通过使用这个Java注解处理器https://github.com/cmoine/generic-enums,你可以写出类似的东西(以convert方法为例):

import org.cmoine.genericEnums.GenericEnum;
import org.cmoine.genericEnums.GenericEnumParam;

@GenericEnum
public enum MyEnum {
    LITERAL1(String.class) {
        @Override
        @GenericEnumParam
        public Object convert(Object o) {
            return o.toString(); // example
        }
    },
    LITERAL2(Integer.class) {
        @Override
        @GenericEnumParam
        public Object convert(Object o) {
            return o.hashCode(); // example
        }
    },
    LITERAL3(Object.class) {
        @Override
        @GenericEnumParam
        public Object convert(Object o) {
            return o; // example
        }
    };

    MyEnum(Class<?> clazz) {
    }

    @GenericEnumParam
    public abstract Object convert(Object o);
}

注解处理器会生成一个枚举MyEnumExt(可定制的),它克服了java枚举的限制。相反,它会生成一个完全可以用作枚举的 Java 类(最后,一个枚举被编译成一个实现 Enum 的 Java 类!)。

答案 6 :(得分:-2)

Becasue“enum”是枚举的缩写。 它只是一组命名常量,代替序数,使代码更易读。

我没有看到类型参数化常量的预期含义是什么。

答案 7 :(得分:-3)

我认为因为基本上Enums不能被实例化

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

枚举是应该始终相同的数据,或者至少是它不会在动态上改变的数据。

新的MyEnum&lt;&gt;()?

以下方法仍然有用

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