如何为事件侦听器模式在 java 中创建和激发 Consumers<T> 的集合

How to create and fire a collection of Consumers<T> in java for an event listener pattern

我正在尝试创建一个非常标准的事件 listener/broadcast 系统。现在只是尝试不同的想法,看看它们在我们的领域中是如何工作的。

我遇到的问题是我似乎无法找到使用泛型调用消费者的正确语法。

private ConcurrentHashMap<Class<? extends Event>, ConcurrentLinkedQueue<Consumer<? extends Event>>> listeners;

public <T extends Event> void listen(Consumer<T> consumer, Class<T> clazz){
    ConcurrentLinkedQueue<Consumer<? extends Event>> consumers = listeners.get(clazz);
    if (consumers == null) {
        consumers = new ConcurrentLinkedQueue<>();
        listeners.put(clazz, consumers);
    }
    consumers.add(consumer);
}

public <T extends Event> void fire(T eventToFire, Class<T> clazz){
    ConcurrentLinkedQueue<Consumer<? extends Event>> consumers = listeners.get(clazz);
    if(consumers != null){
        consumers.forEach(x -> x.accept(eventToFire));// <-- This blows up saying that Consumer expects type T, but I'm passing it ? extends Event
    }
}

调用它的正确方法是什么,或者我是否坚持必须为 ConcurrentLinkedQueue 使用对象?我试过抑制警告,但这似乎是一个更深层次的问题。

那是因为编译器不知道您在 listen 方法中确保映射的键和值都由相同的 T 参数化。就其所知,两者都是 ? extends Event 的两种类型可能彼此不兼容。

解决此问题的唯一方法是使用显式强制转换:

consumers.stream()
        .map(c -> (Consumer<T>)c )
        .forEach(c -> c.accept(eventToFire));

另外注意,listen 方法可以重写为:

listeners.computeIfAbsent(clazz, x -> new ConcurrentLinkedQueue<>())
                .add(consumer);

不仅更短,而且与显式检查 null 不同,它是线程安全的。

此外,fire方法不需要将Class<T>作为参数。您可以使用 listeners.get(eventToFire.getClass())