【发布时间】:2019-09-10 15:49:21
【问题描述】:
我有这两个接口;
public interface Event {
default void dispatch() {
EventBus.getInstance().dispatch(this);
}
}
public interface EventListener<T extends Event> {
void handle(T event);
}
如果我正确理解 Java 中的泛型,我实际上是在告诉第二个接口的继承者将其放入
然后我想出了下一段代码,其中可以注册监听器,可以抛出事件,注册的监听器将处理任何抛出的事件。
public class EventBus {
/**
* The singleton EventBus instance.
*/
private static EventBus instance;
/**
* The map of event types and their listeners.
*/
private final Map<Class<? extends Event>, Set<EventListener<? extends Event>>> listeners = new ConcurrentHashMap<>();
/**
* Create a new EventBus instance.
*/
private EventBus() {
}
/**
* Retrieve the singleton bus instance.
*
* @return The event bus instance.
*/
public static EventBus getInstance() {
if (instance == null) {
instance = new EventBus();
}
return instance;
}
/**
* Register a new event listener, that listens to the given event type.
*
* @param <T>
* @param type The type of event that the given listener should react on.
* @param listener The listener that we want to register.
*/
public <T extends Event> void registerListener(Class<T> type, EventListener<T> listener) {
Set<EventListener<? extends Event>> eventListeners = getOrCreateListeners(type);
eventListeners.add(listener);
}
/**
* Retrieve a set of listeners, either by retrieving an existing list or by creating a new one.
*
* @param eventClass The type of event for which to retrieve listeners.
*
* @return A set of event listeners that listen for the given event.
*/
private Set<EventListener<? extends Event>> getOrCreateListeners(Class<? extends Event> eventClass) {
Set<EventListener<? extends Event>> eventSubscribers = listeners.get(eventClass);
if (eventSubscribers == null) {
eventSubscribers = new CopyOnWriteArraySet<>();
listeners.put(eventClass, eventSubscribers);
}
return eventSubscribers;
}
/**
* Dispatch the given event to all registered listeners associated with that type.
*
* @param <T>
* @param event The event that is to be dispatched.
*/
public <T extends Event> void dispatch(T event) {
listeners.keySet().stream()
.filter(type -> type.isAssignableFrom(event.getClass()))
.flatMap(type -> listeners.get(type).stream())
.forEach(listener -> {
try {
((EventListener<T>) listener).handle(event); // <-- This is where the compiler warns me...
} catch (Exception e) {
Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
}
});
}
}
抛出警告的行在底部附近:((EventListener<T>) listener).handle(event);
事件侦听器的代码大致基于this piece of code。可悲的是,那篇文章没有使用任何泛型。当我为事件和侦听器添加单独的接口时,代码中会出现很多 rawtype 和 unchecked 警告。我开始将一些方法和侦听器 Map 转换为通用方法。我一直在摆弄问号、Ts 等等,试图弄清楚这一点。我已经从编码中学到了很多关于泛型的知识,但我似乎无法弄清楚这一点。
我认为答案可以在以下任一方法中找到 a) 将侦听器 Map 通用化(不知何故?):我想告诉编译器 EventListener<? extends Event> 中的事件类型是 Class<? extends Event> 的类型描述。或者,b) 在发出警告的行上创建一个“安全”强制转换。
我通过这样做(以及更多尝试)尝试了第一个选项:
/**
* The map of event types and their listeners.
*/
private final Map<Class<T extends Event>, Set<EventListener<T>>> listeners = new ConcurrentHashMap<>();
但运气不佳,编译器会告诉你。
我还尝试了第二个选项,添加了以下 if 语句(这里也进行了几次尝试):
if (listener instanceof EventListener<T>) {
((EventListener<T>) listener).handle(event); // <-- This is where the compiler warns me...
}
这也不起作用,因为 T 的类型将在运行时被删除...
也许我很接近,但只是没有使用正确的语法。也许我什至不可能将正确的信息传递给编译器或将其保存在运行时中。也许我什至没有走在正确的轨道上......
【问题讨论】: