【发布时间】:2019-12-13 12:21:55
【问题描述】:
有许多不同的事件,都实现相同的接口:
interface Event {}
class FooEvent implements Event {}
class BarEvent implements Event {}
每个事件都有一个专用的处理程序:
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) { }
}
所有事件处理程序都创建一次并添加到地图中。每当发生事件时,都应使用此映射来找到合适的事件处理程序。
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
}
}
不幸的是,最后一行无法编译。我可以通过省略 EventHandler 的类型参数来编译它:
EventHandlerhandler = this.eventHandlerRegistry.get(event.getClass());
handler.handle(event); // WARNING: unchecked call to 'handle(T)' as a member of raw type 'EventHandler'
但这感觉不太对劲…… 我知道PECS,但我感觉有点被困住了,因为我生产和消费了我的事件处理程序。
我怎样才能干净利落地实现这个?
【问题讨论】:
-
这就是您的事件处理程序的全部用途吗?如果是这样,为什么
EventHandler<T extends Event>是通用的?换句话说,handle(FooEvent event)所增加的价值是什么,而handle(Event event)中不存在? -
每个事件都可以有不同的属性。例如。
FooEvent可以有一个属性String myString而BarEvent可以有一个属性List<String> myBars。具体的处理程序确切地知道他们得到什么类型的事件,并且他们需要该事件的属性来处理它。显然我可以省略EventHandler的通用参数,但是我需要在每个handle()方法中强制转换event,这也感觉不干净...... -
Event是单身人士吗?你能有许多相同类型的事件吗?如果没有,您可以尝试使用Map<Event, EventHandler<? extends Event>> eventHandlerRegistry来映射事件和处理程序。否则,我建议为每种类型的事件提供常量(或枚举)并在地图中使用它们,而不是Class<? extends Event> -
能否分享调用
void handleEvent(Event event)的代码(这个方法可以通用吗?) -
不,
Event不是单例,可以有任意数量的相同类型的事件(例如,对于三明治店,可以有 100 个OrderSandwichEvent)。但我不能使用Event作为映射键,因为并非所有OrderSandwichEvents 都是相等的(例如,一个有boolean ham=true,另一个可以有boolean ham=false)。对于使用常量,我看不出这将如何解决我的问题?在我看来,使用Class<? extends Event>作为映射键不是问题,但通用映射值是......
标签: java generics events collections event-handling