代码之家  ›  专栏  ›  技术社区  ›  Lukas Eder

为什么JavaEnm文字不能具有泛型类型参数?

  •  134
  • Lukas Eder  · 技术社区  · 13 年前

    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 & GT; 同时,作为枚举(不可变、单例、可枚举等)

    问题:

    没人想到这个吗?这是与编译器相关的限制吗?考虑到这个事实, 枚举 “是作为语法糖实现的,它向JVM表示生成的代码,我不理解这一限制。

    谁能向我解释这个?在回答之前,请考虑:

    • 我知道通用类型被删除了:—)
    • 我知道有使用类对象的解决方法。他们是解决办法。
    • 如果适用,泛型类型将导致编译器生成的类型强制转换(例如,在调用convert()方法时)
    • 泛型类型<t>将位于枚举上。因此,它受枚举的每个文本的约束。因此编译器会知道,在编写类似 String string = LITERAL1.convert(myObject); Integer integer = LITERAL2.convert(myObject);
    • 这同样适用于中的泛型类型参数 T getvalue() 方法。编译器可以在调用时应用类型转换 String string = someClass.getValue(LITERAL1)
    7 回复  |  直到 6 年前
        1
  •  46
  •   Lukas Eder    6 年前

    目前正在讨论此问题 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
    

    不幸的是,杰普仍在努力解决重大问题: http://mail.openjdk.java.net/pipermail/amber-spec-experts/2017-May/000041.html

        2
  •  11
  •   Martin Algesten    13 年前

    答案是:

    因为类型擦除

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

    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;
        }
    
        ...
    
    }
    
        3
  •  4
  •   user1944408    9 年前

    枚举中还有其他方法不起作用。什么会 MyEnum.values() 返回?

    怎么样 MyEnum.valueOf(String name) ?

    如果您认为编译器可以使类泛型方法

    public static myenum valueof(字符串名称);

    为了像这样称呼它 MyEnum<String> myStringEnum = MyEnum.value("some string property") 也不行。 例如,如果你打电话 MyEnum<Int> myIntEnum = MyEnum.<Int>value("some string property") ? 实现该方法是不可能正确工作的,例如当您像这样调用它时抛出异常或返回空值。 MyEnum.<Int>value("some double property") 因为类型擦除。

        4
  •  4
  •   Tom Hawtin - tackline    7 年前

    因为你不能。说真的。这可以添加到语言规范中,但还没有。这会增加一些复杂性。成本效益意味着它不是一个高优先级。

    更新:当前正在添加到语言下 JEP 301: Enhanced Enums .

        5
  •  1
  •   nsfyn55    13 年前

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

    JavaEnUM的全部目的是对以相似的属性共享相似类型属性的类型实例的枚举进行建模,该方法提供了可比字符串或整数表示的一致性和丰富性。

    以文本书枚举为例。这不是非常有用或一致的:

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

    为什么我希望我的不同行星有不同的通用类型转换?它能解决什么问题?它是否有理由使语言语义复杂化?如果我真的需要这个行为是一个枚举实现它的最佳工具?

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

    例如

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

    很容易 String Integer 提供名字或序数。但泛型允许您提供任何类型的。您如何管理转换为 MyComplexClass ?现在,通过强制编译器知道可以提供给泛型枚举的类型的有限子集,以及对概念(泛型)引入额外的混淆(泛型),将两个构造搞得一团糟,这似乎已经被许多程序员所回避。

        6
  •  -2
  •   Ingo    13 年前

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

    我不知道类型参数化常量的预期含义是什么。

        7
  •  -3
  •   capsula    13 年前

    我想是因为基本上不能引用枚举

    如果JVM允许,您将在哪里设置T类?

    枚举是应该总是相同的数据,或者至少是不会发生一般性变化的数据。

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