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!
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 know 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
}
}