如何创建和激发消费者群体<;T>;在java中用于事件侦听器模式

如何创建和激发消费者群体<;T>;在java中用于事件侦听器模式,java,events,Java,Events,我正在尝试创建一个非常标准的事件侦听器/广播系统。现在只是玩弄不同的想法,看看它们在我们的领域是如何工作的 我遇到的问题是,我似乎找不到使用泛型调用消费者的正确语法 private ConcurrentHashMap<Class<? extends Event>, ConcurrentLinkedQueue<Consumer<? extends Event>>> listeners; public <T extends Event>

我正在尝试创建一个非常标准的事件侦听器/广播系统。现在只是玩弄不同的想法,看看它们在我们的领域是如何工作的

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

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
    }
}

private ConcurrentHashMap这是因为编译器不知道您在
listen
方法中确保映射的键和值都由相同的
t
参数化。据它所知,这两种类型都是
?扩展事件
可能彼此不兼容

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

consumers.stream()
        .map(c -> (Consumer<T>)c )
        .forEach(c -> c.accept(eventToFire));
不仅如此,与显式检查null不同,它是线程安全的

另外,
fire
方法不需要将
Class
作为参数。您可以使用监听器.get(eventToFire.getClass())

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