将Java Generics与Enums一起使用

更新:感谢所有帮助过的人 – 这个问题的答案在于我在更复杂的代码中没有注意到的内容以及我对Java5协变返回类型的不了解。

原帖:

今天早上我一直在玩弄东西。 虽然我知道我可以用不同的方式解决这个问题,但我发现自己已经痴迷于弄清楚为什么它不像我期望的那样工作。 在花了一些时间阅读这篇文章之后,我发现自己并没有更接近理解,所以我提出这个问题,看看我是不是真的很蠢,或者是否有一些我不明白的事情发生在这里。

我创建了一个自定义事件层次结构,如下所示

public abstract class AbstractEvent<S, T extends Enum> { 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; } } 

具体实现如下:

 public class MyEvent extends AbstractEvent { public enum Type { SELECTED, SELECTION_CLEARED }; public MyEvent(String src, Type t) { super(src, t); } } 

然后我创建一个这样的事件:

 fireEvent(new MyEvent("MyClass.myMethod", MyEvent.Type.SELECTED)); 

我的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; } } } 

所以我认为这将非常简单但事实certificate,对event.getId()的调用会导致编译器告诉我无法启用枚举,只能转换int值或枚举常量。

可以将以下方法添加到MyEvent:

 public Type getId() { return super.getId(); } 

一旦我这样做,一切都按照我的预期完成。 我不只是对找到解决方法感兴趣(因为我显然有一个),我对人们可能有的任何见解感兴趣,为什么这不起作用,因为我预期它会立即起作用。

Yishai是对的,神奇的短语是“ 协变返回类型 ”,这是Java 5.0中的新function – 你不能打开Enum,但是你可以打开扩展Enum的Type类。 由MyEventinheritance的AbstractEvent中的方法受类型擦除的影响。 通过重写它,您将以Java在运行时可以处理的方式将getId()的结果重定向到Type类。

这与generics无关。 java中枚举的switch语句只能使用该特定枚举的值,因此禁止实际指定枚举名。 这应该工作:

 switch(event.getId()) { case SELECTED: l.selected(event); break; case SELECTION_CLEARED: l.unselect(event); break; } 

更新 :好的,这是一个实际的代码(我必须改变一点才能让它编译而没有依赖)我已经复制/粘贴,编译和运行 – 没有错误:

AbstractEvent.java

 public abstract class AbstractEvent> { 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

 public class MyEvent extends AbstractEvent { public enum Type { SELECTED, SELECTION_CLEARED }; public MyEvent(String src, Type t) { super(src, t); } } 

Test.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; } } } 

这在Java 1.5下编译和运行就好了。 我在这里想念的是什么?

适合我。

完整的剪切和粘贴测试程序:

 enum MyEnum { A, B } class Abstract> { private final E e; public Abstract(E e) { this.e = e; } public E get() { return e; } } class Derived extends Abstract { public Derived() { super(MyEnum.A); } public static int sw(Derived derived) { switch (derived.get()) { case A: return 1; default: return 342; } } } 

你在使用一些特殊的编译器吗?

非常简短,因为T被擦除为Enum类,而不是Enum常量,所以编译后的语句看起来像是在将getID的结果作为Enum切换,就像在这个签名中一样:

 Enum getId(); 

当您使用特定类型覆盖它时,您将更改返回值并可以打开它。

编辑:阻力使我好奇,所以我掀起了一些代码:

 public enum Num { ONE, TWO } public abstract class Abstract> { public abstract T getId(); } public abstract class Real extends Abstract { } public static void main(String[] args) throws Exception { Method m = Real.class.getMethod("getId"); System.out.println(m.getReturnType().getName()); } 

结果是java.lang.Enum,而不是Num。 T在编译时被删除为Enum,因此您无法打开它。

编辑:

我测试了每个人都在使用的代码示例,它们也适用于我,所以虽然我怀疑这是更复杂的实际代码中的问题的基础,但发布的样本实际编译并运行正常。

我刚试过这个(复制粘贴你的代码),我无法复制编译器错误。

 public abstract class AbstractEvent> { 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; } } 

 public class MyEvent extends AbstractEvent { public enum Type { SELECTED, SELECTION_CLEARED }; public MyEvent( String src, Type t ) { super( src, t ); } } 

 public class TestMain { protected void fireEvent( MyEvent event ) { switch ( event.getId() ) { case SELECTED: break; case SELECTION_CLEARED: break; } } }