Java通用/通配符类型不匹配 [英] Java generic/wildcard type mismatch
问题描述
我正在尝试构建一个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< ;?扩展EventObject>>>听众;
public Emitter()
{
this.listeners = new HashMap< String,PriorityQueue< Consumer<扩展EventObject>>>();
$ b $ public 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);
返回这个;
}
public< E extends EventObject>发射器发射(E事件)
{
String eventName = event.getClass()。getName();
(Consumer< ;? extends EventObject> listener:this.listeners.get(eventName)){
listener.accept(event);
}
返回此;
}
}
我得到这个编译错误:
Emitter.java:31:错误:不兼容的类型:E无法转换为CAP#1
listener.accept(event);
^
其中E是类型变量:
E扩展在方法< E>中发出的EventObject发送(E)
其中CAP#1是一个新的类型变量:
CAP#1扩展了EventObject的捕获?扩展EventObject
但捕获的类型显然是一个子类型,所以它应该工作(但我理解我'
$ b
使用应该是这样的(当然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
的类型为:消费< ;?扩展EventObject>
(所以,它是一个 Consumer
的某些特定但未知的类型,它扩展了 EventObject
),但您希望它接受 E
类型的事件。编译器无法检查通配符指示的未知类型是否等于类型 E
。 为什么你使用通配符?
public class Emitter< E extends EventObject>这是最好的解决方法。
{
protected HashMap< String,PriorityQueue< Consumer< E>>>听众;
$ b $ public Emitter()
{
this.listeners = new HashMap< String,PriorityQueue< Consumer< E>>>();
公共Emitter on(String eventName,Consumer< E>侦听器)
{
if(!this.listeners.containsKey(eventName)){
this.listeners.put(eventName,new PriorityQueue< Consumer< E>>());
}
this.listeners.get(eventName).add(listener);
返回这个;
}
public Emitter emit(E event)
{
String eventName = event.getClass()。getName(); (Consumer< E> listener:this.listeners.get(eventName)){
listener.accept(event);
。
}
返回此;
注意:通配符类型包含?扩展EventObject
不 意味着你可以传递任何对象给它以扩展 EventObject
;它指定了一个扩展 EventObject
的特定但未知的类型。因为确切类型是什么,是未知的,这限制了你可以用它做什么。
I'm trying to build a Java event emitter, which would have a list of callbacks (implementing Consumer interface) mapped with an event name.
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;
}
}
I get this compile error:
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
but the captured type is clearly a subtype of , so it should work (but I understand I'm missing something).
The use should be something like this (where OpenEvent and CloseEvent extend EventObject of course):
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());
I suppose it's possible to do this type-safe, since I can specify the type of the consumer's object via lambda function. But how?
That happens because listener
is of type: Consumer<? extends EventObject>
(so, it's a Consumer
of some specific, but unknown type that extends EventObject
), but you want it to accept an event of type E
. The compiler cannot check if the unknown type indicated by the wildcard is equal to the type E
.
Why are you using wildcards at all? It would be better to get rid of them, and do something like this:
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;
}
}
Note: A wildcard type with a ? extends EventObject
does not mean you can pass any object to it that extends EventObject
; it specifies one specific, but unknown type that extends EventObject
. Because what the exact type is, is unknown, this limits what you can do with it.
这篇关于Java通用/通配符类型不匹配的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!