我正在尝试构建一个 Java 事件发射器,它将具有一个映射到事件名称的回调列表(实现消费者接口(interface))。
import java.util.HashMap;
import java.util.PriorityQueue;
import java.util.function.Consumer;
import java.util.EventObject;
public class Emitter
{
protected HashMap<String, PriorityQueue<Consumer<? extends EventObject>>> listeners;
public Emitter()
{
this.listeners = new HashMap<String, PriorityQueue<Consumer<? extends EventObject>>>();
}
public Emitter on(String eventName, Consumer<? extends EventObject> listener)
{
if (!this.listeners.containsKey(eventName)) {
this.listeners.put(eventName, new PriorityQueue<Consumer<? extends EventObject>>());
}
this.listeners.get(eventName).add(listener);
return this;
}
public <E extends EventObject> Emitter emit(E event)
{
String eventName = event.getClass().getName();
for (Consumer<? extends EventObject> listener : this.listeners.get(eventName)) {
listener.accept(event);
}
return this;
}
}
我得到这个编译错误:
Emitter.java:31: error: incompatible types: E cannot be converted to CAP#1
listener.accept(event);
^
where E is a type-variable:
E extends EventObject declared in method <E>emit(E)
where CAP#1 is a fresh type-variable:
CAP#1 extends EventObject from capture of ? extends EventObject
但捕获的类型显然是 的子类型,因此它应该可以工作(但我知道我遗漏了一些东西)。
用法应该是这样的(当然 OpenEvent 和 CloseEvent 扩展了 EventObject):
Emitter em = new Emitter();
em.on("open", (OpenEvent e) -> e.doOpen());
em.on("close", (CloseEvent e) -> e.doClose());
em.emit(new OpenEvent());
em.emit(new CloseEvent());
我想可以做到这种类型安全,因为我可以通过 lambda 函数指定消费者对象的类型。但是如何呢?
最佳答案
发生这种情况是因为 listener
是类型:Consumer<? extends EventObject>
(因此,它是一个扩展了 Consumer
的某种特定但未知类型的 EventObject
),但您希望它接受 E
类型的事件.编译器无法检查通配符指示的未知类型是否等于类型 E
.
为什么要使用通配符?最好摆脱它们,然后做这样的事情:
public class Emitter<E extends EventObject>
{
protected HashMap<String, PriorityQueue<Consumer<E>>> listeners;
public Emitter()
{
this.listeners = new HashMap<String, PriorityQueue<Consumer<E>>>();
}
public Emitter on(String eventName, Consumer<E> listener)
{
if (!this.listeners.containsKey(eventName)) {
this.listeners.put(eventName, new PriorityQueue<Consumer<E>>());
}
this.listeners.get(eventName).add(listener);
return this;
}
public Emitter emit(E event)
{
String eventName = event.getClass().getName();
for (Consumer<E> listener : this.listeners.get(eventName)) {
listener.accept(event);
}
return this;
}
}
注意:带有 ? extends EventObject
的通配符类型不是否意味着您可以将任何扩展 EventObject
的对象传递给它;它指定了一个特定但未知的类型,它扩展了 EventObject
.因为确切的类型是什么是未知的,所以这限制了您可以用它做什么。
关于Java 泛型/通配符类型不匹配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23082949/