Java 枚举和泛型

2022-09-04 23:15:46

这件事已经困扰了我一段时间了。我以前问过问题,但可能是措辞不好,例子太抽象了。所以不清楚我到底在问什么。我会再试一次。请不要妄下结论。我希望这个问题根本不容易回答!

为什么我不能在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 ... ]
}

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

public class Utility {

    // Generic methods...
    public static <T> T doStuff(DataType<T> type) {
        // [...]
    }
}

但是这些东西在枚举中是不可能的:

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

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

    // [...]
}

现在,正如我所说。我知道这些东西就是以这种方式设计的。 是句法糖。泛型也是如此。实际上,编译器完成所有工作,并转换为 的子类,将泛型转换为强制转换和综合方法。enumenumsjava.lang.Enum

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

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

public class DataType<T> extends Enum<DataType<?>> {
    // [...]
}

答案 1

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

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

我知道当我试图用Generics获得这种幻想时,我总是处于一个痛苦的世界里,我想语言设计师窥视了那个深渊,并决定不去那里,特别是因为这些功能是同时开发的,这意味着Enum方面有更多的不确定性。

或者换句话说,它将在处理本身具有泛型参数的类时存在所有问题,并且您必须进行大量转换和处理原始类型。对于您正在查看的用例类型,语言设计人员认为这确实是值得的。Class<T>

编辑:为了回应评论(和汤姆 - 投反对票?),嵌套的通用参数使各种坏事发生。Enum 实现了可比性。如果泛型在起作用,那么在客户端代码中比较枚举的两个任意元素根本行不通。一旦处理了泛型参数的泛型参数,最终会遇到各种边界问题和麻烦。很难设计一个能很好地处理它的类。在可比较的情况下,我无法找到一种方法来比较枚举的两个任意成员,而无需恢复到原始类型并收到编译器警告。你能吗?

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

但是,我坚持我答案的要点。汤姆提出了这个问题,当然,我们仍然存在产生问题的问题,在某种程度上,Enum的设计本来可以工作,我们必须意识到这是一个20/20后见之明的事情。Enum是与泛型同时设计的,并且没有验证所有此类角情况的好处。特别是考虑到具有泛型参数的枚举的用例相当有限。(但话又说回来,EnumSet的用例也是如此)。EnumSet.complementOfvalueOf


答案 2

我不认为有广义的枚举是不可能的。如果您可以入侵编译器,则可以拥有一个泛型的Enum子类,并且泛型枚举的类文件不会导致问题。

但最终,枚举几乎是一种语法糖。在 C、C++、C# 中,枚举基本上是 int 常量的别名。Java赋予它更多的功能,但它仍然应该代表简单的项目。

在某个地方,人们必须划清界限。仅仅因为一个类具有枚举实例,并不意味着它必须是枚举。如果它在其他领域足够复杂,它应该成为一个普通的班级。

在你的情况下,做一个枚举没有太大的好处。你可以在开关案例中使用枚举,仅此而已,大不了。非枚举版本工作得很好。DataTypeDataType