Tag*_*gas 5 java generics collections events event-handling
有许多不同的事件,都实现了相同的接口:
interface Event {}
class FooEvent implements Event {}
class BarEvent implements Event {}
Run Code Online (Sandbox Code Playgroud)
每个事件都有一个专用的处理程序:
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) { }
}
Run Code Online (Sandbox Code Playgroud)
所有事件处理程序都创建一次并添加到地图中。每当事件发生时,都应使用此映射来查找正确的事件处理程序。
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
}
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,最后一行不能编译。我可以通过像这样省略 EventHandler 的类型参数来编译它:
EventHandlerhandler = this.eventHandlerRegistry.get(event.getClass());
handler.handle(event); // WARNING: unchecked call to 'handle(T)' as a member of raw type 'EventHandler'
Run Code Online (Sandbox Code Playgroud)
但这感觉不太对劲......我知道PECS,但我觉得有点被困,因为我生产和消费我的 EventHandlers。
我怎样才能干净利落地实现这一点?
如果要在同一个映射中混合使用(通用)处理程序,则无法实现类型安全。据我所知,使代码类型安全的方法是摆脱 ; 上的泛型类型参数EventHandler。但这是您要避免的一件事。
如果您可能会牺牲类型安全性,并且知道您的处理程序将始终与指定的类匹配,那么您可以尝试以下操作:
private <T extends Event> EventHandler<T> getHandler(Class<?> eventClass) {
return (EventHandler<T>)
this.eventHandlerRegistry.get(eventClass); //Unchecked cast
}
Run Code Online (Sandbox Code Playgroud)
然后使你的handleEvent方法通用:
<T extends Event> void handleEvent(T event) {
EventHandler<T> handler = this.getHandler(event.getClass());
handler.handle(event);
}
Run Code Online (Sandbox Code Playgroud)
然后,此方法将成功编译,而不会发出警告。您唯一需要确保的是eventHandlerRegistry永远不会被这样的东西污染:
put(FooEvent.class, new BarEventHandler())); //this can happen
Run Code Online (Sandbox Code Playgroud)