简体   繁体   English

Java通用/通配符类型不匹配

[英]Java generic/wildcard type mismatch

I'm trying to build a Java event emitter, which would have a list of callbacks (implementing Consumer interface) mapped with an event name. 我正在尝试构建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;
    }
}

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): 用法应该是这样的(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());

I suppose it's possible to do this type-safe, since I can specify the type of the consumer's object via lambda function. 我想可以进行这种类型安全的操作,因为我可以通过lambda函数指定使用者对象的类型。 But how? 但是如何?

That happens because listener is of type: Consumer<? extends EventObject> 这是因为listener的类型是: Consumer<? extends EventObject> 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 . Consumer<? extends EventObject>所以,这是一个Consumer ,扩展一些特定的,但不知道类型EventObject ),但你希望它接受类型的事件E The compiler cannot check if the unknown type indicated by the wildcard is equal to the type E . 编译器无法检查该通配符表示未知类型等于类型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 注意:带? extends EventObject通配符类型? extends EventObject ? extends EventObject does not mean you can pass any object to it that extends EventObject ; ? extends EventObject 并不意味着你可以传递任何对象将其延伸EventObject ; it specifies one specific, but unknown type that extends EventObject . 它指定一个扩展特定,但未知类型EventObject Because what the exact type is, is unknown, this limits what you can do with it. 由于确切的类型是什么,还是个未知数,这限制了你可以用它做什么。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM