【问题标题】:EventSource with Map<Class<? extends Event>, List<EventListener<? extends Event>> without unchecked call带有 Map<Class<? 的事件源扩展事件>,列表<EventListener<?扩展事件>> 没有未经检查的调用
【发布时间】:2015-01-07 19:47:07
【问题描述】:

我需要使用每个事件类的侦听器列表来实现观察者模式。

我有:

空界面事件

public interface Event {}

监听器接口:

public interface EventListener<T extends Event> {
void handle(T event);

Class<T> getEventClass();}

和事件来源:

public class EventSource {
private final Map<Class<? extends Event>, List<EventListener<? extends Event>>> listeners = new HashMap<>();

public <T> void subscribe(EventListener<? extends Event> listener) {
    Class<? extends Event> eventClass = listener.getEventClass();
    if (!listeners.containsKey(eventClass)) {
        listeners.put(eventClass, new ArrayList<>());
    }
    listeners.get(eventClass).add(listener);
}

public void unsubscribe(EventListener listener) {
    listeners.remove(listener.getEventClass());
}

public void fire(Event event) {
    for (EventListener listener : listeners.get(event.getClass())) {
        listener.handle(event); //<-- Unchecked call to 'handle(T)' as a member of raw type...
    }
}}

它有效,但我有“未经检查的呼叫”警告。如何避免?

我试过了:

public void fire(Event event) {
    for (EventListener<? extends Event> listener : listeners.get(event.getClass())) {
        listener.handle(event);//<-- compilation error
    }
}

但在这种情况下,我有“句柄不能应用于...”编译错误。

提前致谢!

【问题讨论】:

  • 使用 NetBeans 7.4 我没有收到您的错误,但我收到了:incompatible types: ArrayList&lt;Object&gt; cannot be converted to List&lt;EventListener&lt;? extends Event&gt;&gt; 在您的listeners.put(eventClass, new ArrayList&lt;&gt;());
  • 感谢您的建议。我明白这个问题。在地图,列表>> 编译器不知道“?”在课堂和“?” List 中的类型相同。而且我看不出如何告诉编译器。

标签: java generics observer-pattern unchecked


【解决方案1】:

报告“未经检查的调用”警告是有充分理由的 - 您使用了 EventListener 的原始形式。当您提供类型参数? extends Event 时,它会给出编译错误,因为编译器不会处理EventEventListener - 它只知道它是Event 的特定类型。同样,它不知道event 的运行时类型——它可以是Event 的任何特定类型。

必须知道类型才有意义。我会让EventSource 泛型,用有界泛型类型参数指定Event 的类型。

class EventSource<E extends Event> {

许多? extends Event 声明将更改为使用E,例如

private final Map<Class<? extends Event>, List<EventListener<E>>> listeners = new HashMap<>();

还有许多其他声明将更改为使用E

现在,fire 将采用 E,以便将其传递给 handle

public void fire(E event) {
   for (EventListener<E> listener : listeners.get(event.getClass())) {
      listener.handle(event); // Now this compiles
   }
}

【讨论】:

  • 谢谢,但我不能简单地改变 扩展为 ,因为在这种情况下,我将无法为 EventListener 的子类调用 subscribe()。
【解决方案2】:

改变

listeners.put(eventClass, new ArrayList<>());            

listeners.put(eventClass, new ArrayList<EventListener<? extends Event>>());        

我相信这将使接口方法handle 表现得像预期的那样

【讨论】:

  • 谢谢,但没有任何改变。在 Java 7 (菱形)中是 EventListener 的自动替换。扩展事件>
  • @VitalyChibrikov 我使用的是 Java 7,并且 LHS 运算符上没有定义 菱形运算符,因为您只是使用 put 调用创建 ArrayList。因此您必须显式设置它,否则编译器只会推断为
  • 是的,你是对的。 listeners.put(eventClass, new ArrayList());是 1.8 项目级别,而不是 1.7。正如您所提到的,在 Java 7 中,我需要完整版本的通用初始化。
猜你喜欢
  • 2012-12-26
  • 1970-01-01
  • 1970-01-01
  • 2018-11-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-14
  • 1970-01-01
相关资源
最近更新 更多