问题描述
我有这两个界面;
public interface Event {
default void dispatch() {
EventBus.getInstance().dispatch(this);
}
}
public interface EventListener<T extends Event> {
void handle(T event);
}
如果我正确理解Java中的泛型,实际上是在告诉第二个接口的继承者放置
If I understand generics in Java correctly, I'm practically telling the inheritor of the second interface to put th
然后我想出了下一段代码,可以在其中注册侦听器,可以引发事件,并且已注册的侦听器将处理所有引发的事件.
Then I came up with the next this piece of code, where listeners can be registered, events can be thrown and registered listeners will handle any thrown event.
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);
The line throwing a warning is near the bottom: ((EventListener<T>) listener).handle(event);
事件监听器的代码大致基于这段代码.可悲的是,那篇文章根本没有使用泛型.当我为事件和侦听器添加单独的接口时,代码中会出现很多 rawtype 和 unchecked 警告.我开始将一些方法和侦听器Map转换为通用方法.我一直在摆弄问号,Ts以及更多尝试解决这个问题的方法.我已经从编码这一方面学到了很多有关泛型的知识,但是我似乎无法弄清楚这一点.
The code for the event listener is loosely based on this piece of code. Sadly, that piece doesn't make use of generics whatsoever. When I add separate interfaces for event and listener, a lot of rawtype and unchecked warnings appear in the code. I started turning a few of the methods and the listeners Map into generic ones. I have been fiddling around with question marks, Ts and much more trying to figure this out. I've already learned a lot about generics from coding this but I can't seem to figure this one out.
我认为可以通过以下两种方式找到答案:a)将监听器映射成泛型(以某种方式?):我想告诉编译器 EventListener<中的事件类型?扩展Event>
的类型就是 Class< ;?扩展Event>
描述.或者,b)在发出警告的行上创建安全"强制转换.
I think that the answer can be found in either a) turning the listeners Map generic (somehow?): I want to tell the compiler that the type of event in the EventListener<? extends Event>
is of the type that that Class<? extends Event>
describes. Or, b) creating a "safe" cast on the line that gives a warning.
我通过这样做(以及更多尝试)尝试了第一种选择:
I tried the first option by doing this (and some more attempts):
/**
* The map of event types and their listeners.
*/
private final Map<Class<T extends Event>, Set<EventListener<T>>> listeners = new ConcurrentHashMap<>();
但是运气不好,编译器会告诉您.
But without much luck, as the compiler will tell you.
我还尝试通过添加以下if语句来尝试第二种选择(这里也进行了几次尝试):
I also attempted the second option by adding the following if-statement (a few more attempts were made here as well):
if (listener instanceof EventListener<T>) {
((EventListener<T>) listener).handle(event); // <-- This is where the compiler warns me...
}
这也不起作用,因为T的类型将在运行时删除...
This won't work either, as the type of T will be erased at runtime...
也许我已经接近了,但是只是没有使用正确的语法.也许我什至无法将正确的信息传递给编译器或将其保存在运行时中.也许我什至不在正确的轨道上...
到目前为止,我已经浏览了 this ,和,可惜运气不佳.
So far I've browsed issues like this, this and this one, sadly without much luck.
推荐答案
不幸的是,您知道 .filter(type-> type.isAssignableFrom(event.getClass()))
过滤掉不合适的类型,编译器不知道(也不知道),因此发出警告.
Unfortunately while you know that .filter(type -> type.isAssignableFrom(event.getClass()))
filters out unsuitable types, the compiler doesn't (and can't) know it, hence the warning.
您的侦听器映射具有通用参数,只是这些通用参数不允许您保持完整的(编译时)类型安全.通配符可以确保这一点.您知道(在编译时)该映射包含某些事件的侦听器作为值,但是一旦将侦听器放入其中,就无法再将其取出,而又不会失去编译时类型的安全性.
Your map of listeners has generic parameters that just won't allow you to keep full (compile time) type safety. The wildcards make sure of that. You know (at compile-time) that the map contains listeners for some events as values, but as soon as you put a listener in there, you can't take it out anymore without losing compile-time type safety.
Map<Class<? extends Event>, Set<EventListener<? extends Event>>> listeners
那么您有什么选择?好了,您知道检查事件类型后 是运行时类型安全的,因此您可能只想在方法上标记 @SuppressWarnings
批注并继续.毕竟,这只是警告,以确保您知道自己在做什么.
So what are your options? Well, you know that it is runtime typesafe after you check the event type, so you might just want to tag a @SuppressWarnings
annotation on the method and move on. After all, it's just a warning to make sure you know what you're doing.
这篇关于如何在没有&"unchecked&"的情况下转换为(已知)泛型类型警告?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!