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

标签 java generics types lambda

我正在尝试构建一个 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/

相关文章:

java - 通用双向链表实现

javascript - 当我排除某种类型时, typescript 永远不会显示

java - 什么是 .jspf 文件扩展名?如何编译它?

java - Android 屏幕操纵杆问题

java - 计时器未在预定时间被调用

java - 如何理解泛型方法中选择了什么类型而不是T?

c# - 无法在 C# 中将具体类型转换为其接口(interface)的泛型版本

types - 我如何在 Rust 中打印变量的类型?

c++ - 可以将 std::numeric_limits<T> 专门用于用户定义的类数类吗?

java - 模板类型转换