将 Java 泛型与枚举一起使用

2022-09-02 21:26:23

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

原文:

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

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

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

通过这样的具体实现:

public class MyEvent
extends AbstractEvent<String, MyEvent.Type>
{
    public enum Type { SELECTED, SELECTION_CLEARED };

    public MyEvent(String src, Type t)
    {
        super(src, t);
    }
}

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

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

其中我的火灾事件被定义为:

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

所以我认为这很简单,但事实证明,对event.getId()的调用会导致编译器告诉我,我不能打开枚举,只能打开可转换的int值或枚举常量。

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

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

一旦我这样做了,一切都完全按照我的预期工作。我不只是对找到解决这个问题感兴趣(因为我显然有一个),我对人们可能对为什么这不能像我预期的那样起作用的任何见解感兴趣。


答案 1

Yishai是对的,这个神奇的短语是“协变返回类型”,这是Java 5.0以来的新内容 - 你不能打开Enum,但你可以打开你的Type类来扩展Enum。抽象事件中由 MyEvent 继承的方法需要进行类型擦除。通过重写它,您可以将 的结果重定向到 Type 类,其方式是 Java 可以在运行时处理。getId()


答案 2

这与泛型无关。Java 中 enum 的 switch 语句只能使用该特定枚举的值,因此禁止实际指定枚举名称。这应该有效:

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

更新:好的,这是一个实际的代码(我必须稍微改变一下才能让它在没有依赖项的情况下编译),我已经复制/粘贴,编译和运行 - 没有错误:

摘要事件.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;
    }
}

我的活动.java

public class MyEvent extends AbstractEvent<String, MyEvent.Type> {
    public enum Type { SELECTED, SELECTION_CLEARED };

    public MyEvent(String src, Type t) {
        super(src, t);
    }
}

测试.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下编译和运行就好了。我在这里错过了什么?