我正在尝试构建一个Java事件发射器,它将有一个使用事件名称映射的回调列表(实现Consumer接口)。
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函数指定消费者对象的类型。但是如何?
答案 0 :(得分:3)
之所以会发生这种情况,是因为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
的特定但未知的类型。因为确切的类型是未知的,这限制了你可以用它做什么。