我有这两个接口;
public interface Event {
default void dispatch() {
EventBus.getInstance().dispatch(this);
}
}
public interface EventListener<T extends Event> {
void handle(T event);
}
如果我正确理解 Java 中的泛型,我实际上是在告诉第二个接口的继承者将 th
然后我想出了下一段代码,其中可以注册侦听器,可以引发事件,并且注册的侦听器将处理任何引发的事件。
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);
事件监听器的代码大致基于这段代码 https://github.com/falkoschumann/java-eventbus/blob/master/src/main/java/de/muspellheim/eventbus/EventBus.java。遗憾的是,该作品没有使用任何泛型。当我为事件和侦听器添加单独的接口时,很多rawtype and 未经检查的代码中出现警告。我开始将一些方法和侦听器映射转变为通用方法。我一直在摆弄问号、Ts 等等,试图弄清楚这一点。我已经从编码中学到了很多关于泛型的知识,但我似乎无法弄清楚这一点。
我认为答案可以在以下任一中找到:a)将侦听器映射通用(以某种方式?):我想告诉编译器,事件的类型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 的类型将在运行时被删除......
也许我很接近,但只是没有使用正确的语法。也许我什至不可能将正确的信息传递给编译器或将其保留在运行时。也许我什至没有走在正确的道路上......
到目前为止我已经浏览过类似的问题this https://stackoverflow.com/questions/262367/type-safety-unchecked-cast, this https://codereview.stackexchange.com/questions/3130/how-can-i-avoid-unchecked-cast-warning-in-my-generic-recursive-iterator and this one https://stackoverflow.com/questions/509076/how-do-i-address-unchecked-cast-warnings,遗憾的是没有太多运气。