Java枚举和泛型
这件事困扰了我一段时间。我以前问过,但可能措辞不好,例子太抽象。所以不清楚我到底在问什么。我再试一次。请不要急于下结论。我想这个问题根本不容易回答 为什么不能在Java中使用带有泛型类型参数的枚举? 问题不在于为什么不可能,语法上。我知道这是不受支持的。问题是:为什么JSR人员“忘记”或“忽略”了这个非常有用的特性?我无法想象编译器相关的原因,为什么它不可行 这是我想做的。这在Java中是可能的。这是Java 1.4创建类型安全枚举的方法:Java枚举和泛型,java,generics,enums,Java,Generics,Enums,这件事困扰了我一段时间。我以前问过,但可能措辞不好,例子太抽象。所以不清楚我到底在问什么。我再试一次。请不要急于下结论。我想这个问题根本不容易回答 为什么不能在Java中使用带有泛型类型参数的枚举? 问题不在于为什么不可能,语法上。我知道这是不受支持的。问题是:为什么JSR人员“忘记”或“忽略”了这个非常有用的特性?我无法想象编译器相关的原因,为什么它不可行 这是我想做的。这在Java中是可能的。这是Java 1.4创建类型安全枚举的方法: // A model class for SQL da
// 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 ... ]
}
我就是这么想的- 常规类有实例。您可以创建一个类的新实例,并将其用于某些目的,然后对其进行处置。例如,
List
是一个字符串列表。我可以用字符串做任何我想做的事情,然后当我完成后,我可以用整数做同样的功能
对我来说,枚举数不是创建实例的类型。这和单身汉一样。所以我可以理解为什么JAVA不允许枚举使用泛型,因为您确实无法创建一个新的Enum类型实例来使用临时类。枚举应该是静态的,并且全局只有一个实例。对我来说,为一个全局只有一个实例的类允许泛型是没有意义的
我希望这有帮助 我认为,您希望使用
参数化枚举的原因归结为能够为枚举的各种常量提供不同的方法签名
在您的示例中,parse
的签名(参数类型和返回类型)是:
- 对于
:Datatype.INT
INT解析(字符串)
- 对于
:Datatype.VARCHAR
字符串解析(String)
- 等等
Datatype type = ...
...
int x = type.parse("45");
要对此类表达式应用静态类型和类型检查,所有实例的方法签名必须相同。但是,最后您建议对不同的实例使用不同的方法签名。。。这就是为什么在Java中不可能做到这一点。我想猜测一下,这是因为Enum类本身的类型参数存在协方差问题,它被定义为
Enum
,尽管要研究所有这种情况有点多
除此之外,枚举的一个主要用例是使用EnumSet和valueOf之类的东西,其中您有一组具有不同泛型参数的东西,并从字符串中获取值,所有这些都不支持枚举本身的泛型参数,甚至更糟
我知道,当我试图对泛型产生幻想时,我总是处于一个痛苦的世界中,我想象着语言设计者看到了那个深渊并决定不去那里,特别是因为这些特性是同时开发的,这意味着枚举方面的事情更加不确定
或者换句话说,它在处理本身具有泛型参数的类时会遇到Class
的所有问题,并且您必须进行大量的转换和处理原始类型。对于您正在查看的用例类型,语言设计者认为不值得这样做
编辑:作为对评论的回应(还有Tom-一个否决票?),嵌套的泛型参数会导致各种不好的事情发生。枚举实现了可比较的。若使用泛型,那个么在客户机代码中比较枚举的两个任意元素是行不通的。一旦处理泛型参数中的泛型参数,就会遇到各种各样的边界问题和麻烦。很难设计一个能够很好地处理它的类。在comparable的情况下,我无法找到一种方法来比较枚举的两个任意成员,而不返回原始类型并获得编译器警告。你能吗
事实上,上面的错误是令人尴尬的,因为我使用问题中的数据类型作为思考这个问题的模板,但事实上枚举会有一个子类,所以这并不完全正确
然而,我坚持我回答的要点。Tom提出了EnumSet.complementOf
,当然我们仍然有产生问题的valueOf
,在一定程度上,Enum的设计是可行的,我们必须意识到这是事后诸葛亮的事情。Enum是与泛型同时设计的,并没有验证所有这些角落案例的好处。特别是考虑到带有泛型参数的枚举的用例相当有限。(但同样,EnumSet的用例也是如此)。公共enum GenericEnum{
简单,复杂,;
公共T解析(字符串s){
返回T.parse(s);
}
}
公共无效剂量测定法(){
GenericEnum longGE=GenericEnum.SIMPLE;
GenericEnum intGE=GenericEnum.SIMPLE;
List longList=新的LinkedList();
List intList=新建LinkedList();
断言(longGE==intGE);//16
断言(stringList.equals(intList));//17
对象x=longGE.parse(“1”);//19
}
第16行和第17行的断言都是真实的。泛型类型在运行时不可用
枚举的优点之一是可以使用==来比较它们。第16行的断言将计算为true
但是在19号线我们遇到了一个问题。longGE和intGE是同一个对象(如第16行的断言所示)。解析(“1”)将返回什么?泛型类型信息在运行时不可用。因此,在运行时无法确定解析方法的T
枚举基本上是静态的,它们只存在一次。应用泛型是没有意义的
// This can't be done
public enum DataType<T> {
// Neither can this...
INT<Integer>("int", Integer.class),
INT4<Integer>("int4", Integer.class),
// [...]
}
public class DataType<T> extends Enum<DataType<?>> {
// [...]
}
Datatype type = ...
...
int x = type.parse("45");
public enum GenericEnum<T> {
SIMPLE, COMPLEX;
public T parse(String s) {
return T.parse(s);
}
}
public void doSomething() {
GenericEnum<Long> longGE = GenericEnum<Long>.SIMPLE;
GenericEnum<Integer> intGE = GenericEnum<Integer>.SIMPLE;
List<Long> longList = new LinkedList<Long>();
List<Integer> intList = new LinkedList<Integer>();
assert(longGE == intGE); // 16
assert(stringList.equals(intList)); // 17
Object x = longGE.parse("1"); // 19
}