将 Java 泛型与枚举一起使用
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1173290/
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
Using Java Generics with Enums
提问by Chris Boran
Update:Thanks for everyone who helped out - the answer to this one lay in what I wasn't noticing in my more complex code and what I didn't know about the Java5 covariant return types.
更新:感谢所有提供帮助的人 - 这个问题的答案在于我在更复杂的代码中没有注意到的内容以及我对 Java5 协变返回类型不了解的内容。
Original Post:
原帖:
I've been playing around with something this morning. While I know that I couldtackle this whole problem differently, I'm finding myself obsessed with figuring out why it isn't working the way that I was expecting. After spending some time reading around on this, I find I'm no closer to understanding, so I offer it up as a question to see if I'm just being stupid or if there is really something I don't understand going on here.
今天早上我一直在玩什么。虽然我知道我可以以不同的方式解决整个问题,但我发现自己沉迷于弄清楚为什么它没有按我预期的方式工作。在花了一些时间阅读之后,我发现我并没有更接近于理解,所以我将它作为一个问题来提供,看看我是否只是愚蠢或者是否真的有一些我不理解的事情发生.
I have created a custom Event hierarchy like so:
我创建了一个自定义事件层次结构,如下所示:
public abstract class AbstractEvent<S, T extends Enum<T>>
{
private S src;
private T id;
public AbstractEvent(S src, T id)
{
this.src = src;
this.id = id;
}
public S getSource()
{
return src;
}
public T getId()
{
return id;
}
}
With a concrete implementation like so:
像这样的具体实现:
public class MyEvent
extends AbstractEvent<String, MyEvent.Type>
{
public enum Type { SELECTED, SELECTION_CLEARED };
public MyEvent(String src, Type t)
{
super(src, t);
}
}
And then I create an event like so:
然后我创建一个这样的事件:
fireEvent(new MyEvent("MyClass.myMethod", MyEvent.Type.SELECTED));
Where my fireEvent is defined as:
我的 fireEvent 定义为:
protected void fireEvent(MyEvent event)
{
for(EventListener l : getListeners())
{
switch(event.getId())
{
case SELECTED:
l.selected(event);
break;
case SELECTION_CLEARED:
l.unselect(event);
break;
}
}
}
So I thought that this would be pretty straightforward but it turns out that the call to event.getId() results in the compiler telling me that I cannot switch on Enums, only convertible int values or enum constants.
所以我认为这会非常简单,但事实证明对 event.getId() 的调用导致编译器告诉我我不能打开枚举,只能打开可转换的 int 值或枚举常量。
It is possible to add the following method to MyEvent:
可以将以下方法添加到 MyEvent:
public Type getId()
{
return super.getId();
}
Once I do this, everything works exactly as I expected it to. I'm not just interested in finding a workaround for this (because I obviously have one), I'm interested in any insight people might have as to WHY this doesn't work as I expected it to right off the bat.
一旦我这样做了,一切都按照我的预期工作。我不仅对为此寻找解决方法感兴趣(因为我显然有一个解决方法),而且我对人们可能对为什么这不起作用的任何见解感兴趣。
采纳答案by Jason S
Yishai is right, and the magic phrase is "covariant return types" which is new as of Java 5.0 -- you can't switch on Enum, but you can switch on your Type class which extends Enum. The methods in AbstractEvent that are inherited by MyEvent are subject to type erasure. By overriding it, you're redirecting the result of getId()
towards your Type class in a way that Java can handle at run-time.
Yishai 是对的,神奇的短语是“协变返回类型”,这是 Java 5.0 中的新内容——您无法打开 Enum,但您可以打开扩展 Enum 的 Type 类。由 MyEvent 继承的 AbstractEvent 中的方法受类型擦除的影响。通过覆盖它,您getId()
将以 Java 可以在运行时处理的方式将 的结果重定向到您的 Type 类。
回答by ChssPly76
This is not related to generics. Switch statement for enum in java can only use values of that particular enum, thus it's prohibitedto actually specify enum name. This should work:
这与泛型无关。java 中 enum 的 switch 语句只能使用该特定 enum 的值,因此禁止实际指定 enum 名称。这应该有效:
switch(event.getId()) {
case SELECTED:
l.selected(event);
break;
case SELECTION_CLEARED:
l.unselect(event);
break;
}
Update: Ok, here's an actual code (which I had to change a little bit to get it to compile with no dependencies) that I've copy / pasted, compiled and ran - no errors:
更新:好的,这是我复制/粘贴、编译和运行的实际代码(我必须对其进行一些更改才能使其在没有依赖项的情况下进行编译) - 没有错误:
AbstractEvent.java
抽象事件.java
public abstract class AbstractEvent<S, T extends Enum<T>> {
private S src;
private T id;
public AbstractEvent(S src, T id) {
this.src = src;
this.id = id;
}
public S getSource() {
return src;
}
public T getId() {
return id;
}
}
MyEvent.java
我的事件.java
public class MyEvent extends AbstractEvent<String, MyEvent.Type> {
public enum Type { SELECTED, SELECTION_CLEARED };
public MyEvent(String src, Type t) {
super(src, t);
}
}
Test.java
测试.java
public class Test {
public static void main(String[] args) {
fireEvent(new MyEvent("MyClass.myMethod", MyEvent.Type.SELECTED));
}
private static void fireEvent(MyEvent event) {
switch(event.getId()) {
case SELECTED:
System.out.println("SELECTED");
break;
case SELECTION_CLEARED:
System.out.println("UNSELECTED");
break;
}
}
}
This compiles and runs under Java 1.5 just fine. What am I missing here?
这在 Java 1.5 下编译和运行就好了。我在这里缺少什么?
回答by Yishai
In very short, because T is erased to an Enum class, not an Enum constant, so the compiled statement looks like you are switching on the result of getID being Enum, as in this signature:
简而言之,因为 T 被擦除到一个 Enum 类,而不是一个 Enum 常量,所以编译的语句看起来你正在打开 getID 的结果是 Enum,如在这个签名中:
Enum getId();
When you override it with a specific type, then you are changing the return value and can switch on it.
当您使用特定类型覆盖它时,您正在更改返回值并可以打开它。
EDIT: The resistance made me curious, so I whipped up some code:
编辑:阻力让我很好奇,所以我写了一些代码:
public enum Num {
ONE,
TWO
}
public abstract class Abstract<T extends Enum<T>> {
public abstract T getId();
}
public abstract class Real extends Abstract<Num> {
}
public static void main(String[] args) throws Exception {
Method m = Real.class.getMethod("getId");
System.out.println(m.getReturnType().getName());
}
public enum Num {
ONE,
TWO
}
public abstract class Abstract<T extends Enum<T>> {
public abstract T getId();
}
public abstract class Real extends Abstract<Num> {
}
public static void main(String[] args) throws Exception {
Method m = Real.class.getMethod("getId");
System.out.println(m.getReturnType().getName());
}
The result is java.lang.Enum, not Num. T is erased to Enum at compile time, so you can't switch on it.
结果是 java.lang.Enum,而不是 Num。T 在编译时被擦除为 Enum,因此您无法打开它。
EDIT:
编辑:
I tested the code samples everyone is working with, and they work for me as well, so although I suspect that this underlies the issue in the more complex real code, the sample as posted actually compiles and runs fine.
我测试了每个人都在使用的代码示例,它们也对我有用,所以尽管我怀疑这是更复杂的实际代码中问题的根源,但发布的示例实际上编译并运行良好。
回答by Trevor Harrison
I just tried this (copy-pasted your code) and I can't duplicate a compiler error.
我刚刚试过这个(复制粘贴你的代码),我无法复制编译器错误。
public abstract class AbstractEvent<S, T extends Enum<T>>
{
private S src;
private T id;
public AbstractEvent(S src, T id)
{
this.src = src;
this.id = id;
}
public S getSource()
{
return src;
}
public T getId()
{
return id;
}
}
and
和
public class MyEvent extends AbstractEvent<String, MyEvent.Type>
{
public enum Type
{
SELECTED, SELECTION_CLEARED
};
public MyEvent( String src, Type t )
{
super( src, t );
}
}
and
和
public class TestMain
{
protected void fireEvent( MyEvent event )
{
switch ( event.getId() )
{
case SELECTED:
break;
case SELECTION_CLEARED:
break;
}
}
}
回答by Tom Hawtin - tackline
Works for me.
为我工作。
Complete cut-out-and-paste test program:
完整的剪切和粘贴测试程序:
enum MyEnum {
A, B
}
class Abstract<E extends Enum<E>> {
private final E e;
public Abstract(E e) {
this.e = e;
}
public E get() {
return e;
}
}
class Derived extends Abstract<MyEnum> {
public Derived() {
super(MyEnum.A);
}
public static int sw(Derived derived) {
switch (derived.get()) {
case A: return 1;
default: return 342;
}
}
}
Are you using some peculiar compiler?
你在使用一些特殊的编译器吗?