EventSource with Map<Class<? extends Event>, List<EventListener<? extends Event>> without unchecked call

馋奶兔 提交于 2019-12-11 03:05:06

问题


I need to implement Observer pattern with List of listeners per Event class.

I have:

empty interface Event

public interface Event {}

interface for Listeners:

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

Class<T> getEventClass();}

and event source:

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

It works, but I have "Unchecked call" warning. How to avoid it?

I tried:

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

but in this case I have "handle cannot be applied to..." compilation error.

Thanks in advance!


回答1:


The "unchecked call" warning was reported for a good reason - you used the raw form of EventListener. When you gave a type parameter ? extends Event, it gave the compilation error because the compiler doesn't which Event the EventListener handles - it only knows that it is a specific type of Event. Likewise, it doesn't know the runtime type of event - it could be any specific type of Event.

The type must be known for this to make sense. I would make EventSource generic, specifying the type of Event with a bounded generic type parameter.

class EventSource<E extends Event> {

Many ? extends Event declarations will change to use E, e.g.

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

There are many other declarations that will change to use E.

Now, fire will take an E so that it can be passed to handle:

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



回答2:


Change

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

to

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

I believe that will make the interface method handle behave as is supposed to



来源:https://stackoverflow.com/questions/27827435/eventsource-with-mapclass-extends-event-listeventlistener-extends-event

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!