Java枚举和泛型

Luk*_*der 13 java generics enums

这件事现在困扰我一段时间了.我以前曾问过问题,但可能是一个错误的措辞和一个过于抽象的例子.所以我不清楚我究竟在问什么.我会再尝试.请不要妄下结论.我希望这个问题根本不容易回答!

为什么我不能在Java中使用泛型类型参数的枚举?

问题不在于为什么它在语法上是不可能的.我知道它不受支持.问题是:为什么JSR人员"忘记"或"省略"这个非常有用的功能?我无法想象与编译器相关的原因,为什么它不可行.

这就是我喜欢做的事情.这在Java中是可行的.这是创建类型安全枚举的Java 1.4方法:

// A model class for SQL data types and their mapping to Java types
public class DataType<T> implements Serializable, Comparable<DataType<T>> {
    private final String name;
    private final Class<T> type;

    public static final DataType<Integer> INT      = new DataType<Integer>("int", Integer.class);
    public static final DataType<Integer> INT4     = new DataType<Integer>("int4", Integer.class);
    public static final DataType<Integer> INTEGER  = new DataType<Integer>("integer", Integer.class);
    public static final DataType<Long>    BIGINT   = new DataType<Long>   ("bigint", Long.class);    

    private DataType(String name, Class<T> type) {
        this.name = name;
        this.type = type;
    }

    // Returns T. I find this often very useful!
    public T parse(String string) throws Exception {
        // [...]
    }

    // Check this out. Advanced generics:
    public T[] parseArray(String string) throws Exception {
        // [...]
    }

    // Even more advanced:
    public DataType<T[]> getArrayType() {
        // [...]
    }

    // [ ... more methods ... ]
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以<T>在许多其他地方使用

public class Utility {

    // Generic methods...
    public static <T> T doStuff(DataType<T> type) {
        // [...]
    }
}
Run Code Online (Sandbox Code Playgroud)

但这些东西不可能用枚举:

// This can't be done
public enum DataType<T> {

    // Neither can this...
    INT<Integer>("int", Integer.class), 
    INT4<Integer>("int4", Integer.class), 

    // [...]
}
Run Code Online (Sandbox Code Playgroud)

现在,正如我所说.我知道这些东西都是按照这种方式设计的.enum是语法糖.仿制药也是如此.实际上,编译器完成所有工作并将其转换enums为转换子类java.lang.Enum和泛型转换为强制转换和合成方法.

但为什么编译器不能进一步允许通用枚举?

编辑:这是我期望的编译器生成的Java代码:

public class DataType<T> extends Enum<DataType<?>> {
    // [...]
}
Run Code Online (Sandbox Code Playgroud)

Yis*_*hai 4

我会猜测一下,并说这是因为 Enum 类本身的类型参数的协方差问题,它被定义为Enum<E extends Enum<E>>,尽管调查它的所有极端情况有点太多。

除此之外,枚举的主要用例是像 EnumSet 和 valueOf 这样的东西,你有一个具有不同泛型参数的集合,并从字符串中获取值,所有这些都不会支持或更糟枚举本身的泛型参数。

我知道,当我尝试使用泛型时,我总是处于痛苦的世界中,我想象语言设计者看到了那个深渊,并决定不去那里,特别是因为这些功能是同时开发的,这意味着甚至Enum 方面的不确定性更大。

或者换句话说,它会遇到Class<T>处理本身具有泛型参数的类的所有问题,并且您必须进行大量的转换和处理原始类型。语言设计者认为对于您正在查看的用例类型而言,这并不是真正值得的事情。

编辑:为了回应评论(和汤姆 - 否决票?),嵌套通用参数会导致各种不好的事情发生。Enum 实现了 Comparable。如果使用泛型,则根本无法比较客户端代码中枚举的两个任意元素。一旦处理了泛型参数的泛型参数,您最终会遇到各种边界问题和令人头痛的问题。设计一个能够很好地处理它的类是很困难的。在可比较的情况下,我无法找到一种方法来使其可以比较枚举的两个任意成员,而无需恢复为原始类型并获得编译器警告。您可以...吗?

实际上,上面的内容是令人尴尬的错误,因为我使用问题中的 DataType 作为思考这个问题的模板,但实际上 Enum 会有一个子类,所以这不太正确。

不过,我坚持我的回答的要点。汤姆提出了EnumSet.complementOf,当然我们仍然存在valueOf产生问题的问题,并且就 Enum 的设计可能起作用的程度而言,我们必须意识到这是一个 20/20 事后诸葛亮的事情。枚举是与泛型同时设计的,并且没有验证所有此类极端情况的好处。特别是考虑到具有通用参数的 Enum 的用例相当有限。(但话又说回来,EnumSet 的用例也是如此)。