Java 枚举和泛型
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5108733/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java enums and generics
提问by Lukas Eder
This thing is troubling me for a while now. I have asked questionsbefore, but probably with a bad phrasing and an example that was too abstract. So it wasn't clear what I was actually asking. I'll try again. And please don't jump to conclusions. I expect that the question is not easy at all to answer!
这件事让我困扰了一段时间。我之前问过问题,但可能是措辞不当和示例过于抽象。所以不清楚我实际上在问什么。我会再尝试。并且请不要急于下结论。我希望这个问题根本不容易回答!
why can't I have an enum with generic type parameters in Java?
为什么我不能在 Java 中使用带有泛型类型参数的枚举?
The question is not about why it's not possible, syntactically. I know it's just not supported. The question is: why did the JSR people "forget" or "omit" this very useful feature? I can't imagine a compiler-related reason, why it wouldn't be feasible.
问题不在于为什么在语法上不可能。我知道它只是不受支持。问题是:为什么 JSR 人“忘记”或“省略”了这个非常有用的特性?我无法想象与编译器相关的原因,为什么它不可行。
Here's what I would love to do. This is possible in Java. It's the Java 1.4 way to create typesafe enums:
这就是我想做的事情。这在 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 ... ]
}
And then, you could use <T>
in many other places
然后,您可以<T>
在许多其他地方使用
public class Utility {
// Generic methods...
public static <T> T doStuff(DataType<T> type) {
// [...]
}
}
But these things are not possible with an enum:
但是这些事情对于枚举是不可能的:
// This can't be done
public enum DataType<T> {
// Neither can this...
INT<Integer>("int", Integer.class),
INT4<Integer>("int4", Integer.class),
// [...]
}
Now, as I said. I know these things have been designed exactly that way. enum
is syntactic sugar. So are generics. Actually, the compiler does all the work and transforms enums
into subclasses of java.lang.Enum
and generics into casts and synthetic methods.
现在,正如我所说。我知道这些东西就是这样设计的。enum
是语法糖。泛型也是如此。实际上,编译器完成了所有工作enums
,java.lang.Enum
并将泛型转换为类型转换和合成方法的子类和泛型。
but why can't the compiler go further and allow for generic enums??
但是为什么编译器不能更进一步并允许通用枚举?
EDIT: This is what I would expect as compiler-generated Java code:
编辑:这是我期望的编译器生成的 Java 代码:
public class DataType<T> extends Enum<DataType<?>> {
// [...]
}
采纳答案by Yishai
I'm going to guess a bit and say that it is because of covariance issues on the type parameter of the Enum class itself, which is defined as Enum<E extends Enum<E>>
, although it is a bit much to investigate all the corner cases of that.
我会猜测一下,说这是因为 Enum 类本身的类型参数的协方差问题,它被定义为Enum<E extends Enum<E>>
,尽管调查它的所有极端情况有点多。
Besides that, a primary use case of enums is with things like EnumSet and valueOf where you have a collection of things with different generic parameters and get the value from a string, all of which would not support or worse the generic parameter on the enum itself.
除此之外,枚举的一个主要用例是使用 EnumSet 和 valueOf 之类的东西,其中您有一组具有不同泛型参数的事物并从字符串中获取值,所有这些都不支持或更糟的是枚举本身的泛型参数.
I know I'm always in a world of pain when I try to get that fancy with Generics, and I imagine the language designers peeked at that abyss and decided to not go there, especially since the features were developed concurrently, which would mean even more uncertainty for the Enum side of things.
我知道当我试图对泛型产生幻想时,我总是处于痛苦的世界中,我想象语言设计者偷看了那个深渊并决定不去那里,特别是因为这些功能是同时开发的,这意味着即使枚举方面的更多不确定性。
Or put another way, it would have all the problems of Class<T>
in dealing with classes which themselves have generic parameters, and you would have to do a lot of casting and dealing with raw types. Not truly something that the language designers felt was worth it for the type of use case you are looking at.
或者换句话说,它会遇到Class<T>
处理本身具有泛型参数的类的所有问题,并且您将不得不进行大量的转换和处理原始类型。对于您正在查看的用例类型,语言设计人员认为这并不是真正值得的。
EDIT: In response to the comments (and Tom - a downvote?), nested generic parameter makes all kinds of bad things happen. Enum implements Comparable. That simply would not work to compare two arbitrary elements of the enum in client code if generics were in play. Once you deal with a Generic parameter of a Generic parameter, you end up with all kinds of bounds problems and headaches. It is hard to design a class that handles it well. In the case of comparable, I could not figure out a way to make it work to compare two arbitrary members of an enum without reverting to raw types and getting a compiler warning. Could you?
编辑:为了回应评论(和汤姆 - 反对票?),嵌套的泛型参数会导致各种不好的事情发生。枚举实现了 Comparable。如果泛型在起作用,那根本无法比较客户端代码中枚举的两个任意元素。一旦处理了 Generic 参数的 Generic 参数,最终会遇到各种边界问题和头痛。很难设计一个处理它的类。在可比较的情况下,我无法找到一种方法来比较枚举的两个任意成员,而不会恢复到原始类型并收到编译器警告。您可以...吗?
Actually the above is embarrassingly wrong, as I was using the DataType in the question as my template for thinking about this, but in fact an Enum would have a subclass, so that isn't quite right.
实际上,上面是令人尴尬的错误,因为我使用问题中的 DataType 作为我思考这个问题的模板,但实际上 Enum 会有一个子类,所以这不太正确。
However, I stand by the gist of my answer. Tom brought up EnumSet.complementOf
and of course we still have valueOf
that produces problems, and to the degree that the design of Enum could have worked, we have to realize that that is a 20/20 hindsight thing. Enum was being designed concurrently with generics and didn't have the benefit of validating all such corner cases. Especially considering that the use case for an Enum with a generic parameter is rather limited. (But then again, so is the use case for EnumSet).
但是,我坚持我的回答的要点。汤姆提出了EnumSet.complementOf
,当然我们仍然有valueOf
产生问题的问题,并且就 Enum 的设计可以工作的程度而言,我们必须意识到这是 20/20 事后诸葛亮的事情。Enum 是与泛型同时设计的,并且没有验证所有这些极端情况的好处。特别是考虑到具有泛型参数的 Enum 的用例相当有限。(但话又说回来,EnumSet 的用例也是如此)。
回答by irreputable
I don't think it is impossible to have generified enum. If you could hack into compiler, you can have a subclass of Enum that is generic, and the class file of your generic enum wouldn't cause problems.
我认为不可能有泛型枚举。如果您可以侵入编译器,您可以拥有一个通用的 Enum 子类,并且您的通用枚举的类文件不会引起问题。
But in the end, enum is pretty much a syntax sugar. In C, C++, C#, enums are basically alias for int constants. Java gives it more power, but it is still supposedto represent simple items.
但归根结底,枚举几乎是一种语法糖。在 C、C++、C# 中,枚举基本上是 int 常量的别名。Java 赋予它更多的功能,但它仍然应该表示简单的项目。
Somewhere people have to draw the line. Just because a class has enumerated instances, doesn't mean it must be an enum. If it is sophisticated enough in other areas, it deserves to be a regular class.
人们必须在某个地方划清界限。仅仅因为一个类具有枚举实例,并不意味着它必须是一个枚举。如果它在其他领域足够复杂,那么它应该是一个普通的类。
In your case, there is not much advantage to make DataType
an enum. You can use enum in switch-case, that's about it, big deal. The non-enum verion of DataType
works just fine.
在您的情况下,进行DataType
枚举没有太大优势。你可以在 switch-case 中使用 enum,就是这样,很重要。非枚举版本的DataType
作品就好了。
回答by Amir Raminfar
This is how I think of it -
我是这样想的——
Regular classes have instances. You create a new instance of a class use it for some purpose and then dispose it. For example List<String>
is a list of strings. I can do what ever I want to do with strings and then when I am done I can later do the same functionality with integers.
常规类有实例。您创建一个类的新实例,将其用于某种目的,然后将其处理掉。例如List<String>
是一个字符串列表。我可以用字符串做任何我想做的事情,然后当我完成后我可以用整数做同样的功能。
To me enumerators are not types that you create instances of. Its same thing as a singleton. So I can see why JAVA wouldn't allow generics for Enums because you really can't create a new instance of type Enum to use temporary like you do with classes. Enums are supposed to be static and only have one instance globally. To me, it wouldn't make sense to allow generics for a class that only has one instance globally.
对我来说,枚举器不是您创建实例的类型。它与单身人士相同。所以我可以理解为什么 JAVA 不允许 Enum 的泛型,因为你真的不能像对类那样创建一个新的 Enum 类型的实例来使用临时的。枚举应该是静态的,并且全局只有一个实例。对我来说,为一个全局只有一个实例的类允许泛型是没有意义的。
I hope this helps.
我希望这有帮助。
回答by ChrisJ
I think that the reason why you wish to parameterize the enum with <T>
boils down to being able to have different method signatures for the various constants of the enum.
我认为您希望使用枚举参数化的原因<T>
归结为能够为枚举的各种常量使用不同的方法签名。
In your example, the signature (type of parameters and return type) for parse
would be:
在您的示例中,签名(参数类型和返回类型)为parse
:
- for
Datatype.INT
:int parse(String)
- for
Datatype.VARCHAR
:String parse(String)
- and so on
- 对于
Datatype.INT
:int parse(String)
- 对于
Datatype.VARCHAR
:String parse(String)
- 等等
So how would the compiler be able to typecheck something like:
那么编译器如何能够进行类型检查,例如:
Datatype type = ...
...
int x = type.parse("45");
???
???
To apply static typing and typechecking to this kind of expression, the signature of the method must be the same for all the instances. However, in the end you suggest to have different method signatures for different instances... That's why it's not possible to do it in Java.
要将静态类型和类型检查应用于此类表达式,所有实例的方法签名必须相同。但是,最后您建议为不同的实例使用不同的方法签名......这就是为什么不可能在 Java 中做到这一点。
回答by MrJacqes
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
}
The asserts at line 16 and 17 are both true. The generic types are not available at run time.
第 16 行和第 17 行的断言都是正确的。泛型类型在运行时不可用。
One of the advantages of an enum is that you can use == to compare them. The assert at line 16 will evaluate to true.
枚举的优点之一是您可以使用 == 来比较它们。第 16 行的断言将评估为真。
At line 19 we run into a problem though. longGE and intGE are the same object (as the assert at line 16 shows.) What will be returned by the parse("1")? The generic type information is not available at run time. So there would be no way to determine T for the parse method at run time.
在第 19 行,我们遇到了一个问题。longGE 和 intGE 是同一个对象(如第 16 行的断言所示。) parse("1") 将返回什么?通用类型信息在运行时不可用。因此无法在运行时确定 parse 方法的 T。
Enums are basically static, they only exist once. And it doesn't make sense to apply generic typing to static types.
枚举基本上是静态的,它们只存在一次。将泛型类型应用于静态类型是没有意义的。
I hope this helps.
我希望这有帮助。
Note - this is not meant to be working code. It is using the syntax suggested in the original question.
注意 - 这不是工作代码。它使用原始问题中建议的语法。