There are many different events, all implementing the same interface:
interface Event {}
class FooEvent implements Event {}
class BarEvent implements Event {}
Every event has a dedicated handler:
interface EventHandler<T extends Event> {
void handle(T event);
}
class FooEventHandler implements EventHandler<FooEvent> {
@Override
public void handle(FooEvent event) { }
}
class BarEventHandler implements EventHandler<BarEvent> {
@Override
public void handle(BarEvent event) { }
}
All event handlers are created once and added to a map. Whenever an event occurs, this map should be used to find the proper event hander.
class Main {
Map<Class<? extends Event>, EventHandler<? extends Event>> eventHandlerRegistry = Map.of(
FooEvent.class, new FooEventHandler(),
BarEvent.class, new BarEventHandler()
);
void handleEvent(Event event) {
EventHandler<? extends Event> handler = this.eventHandlerRegistry.get(event.getClass());
handler.handle(event); // DOES NOT COMPILE: needed=capture<? extends Event>, given=Event
}
}
Unfortunately this last line does not compile. I can make it compile by leaving out the type parameter of EventHandler like this:
EventHandlerhandler = this.eventHandlerRegistry.get(event.getClass());
handler.handle(event); // WARNING: unchecked call to 'handle(T)' as a member of raw type 'EventHandler'
But this does not quite feel right... I am aware of PECS, but I feel kind of trapped because I produce AND consume my EventHandlers.
How can I implement this cleanly?