java - 如何在带有通配符的泛型映射中使用带有类型化参数的泛型集合 (Java)

标签 java events generics

此问题与 How to create and fire a collection of Consumers<T> in java for an event listener pattern 相关。我试图使用事件集合来帮助批量操作,但到目前为止我似乎无法提供通用定义的正确配置。

这是到目前为止我的代码。

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

public <T extends Event> void listen(Class<T> clazz, Consumer<Collection<T>> consumer){
    ConcurrentLinkedQueue<Consumer<Collection<T>>> consumers = listeners.get(clazz);
    if (consumers == null) {
        consumers = new ConcurrentLinkedQueue<>();
        listeners.put(clazz, consumers); // Complains that consumers is not the type Collection<? extends Event>
    }
    consumers.add(consumer);
}

public <T extends Event> void fire(Collection<T> eventToFire){
    ConcurrentLinkedQueue<Consumer<Collection<? extends Event>>> consumers = listeners.get(eventToFire.getClass());
    if(consumers != null){
        consumers.forEach(x -> x.accept(eventToFire));
    }
}

我尝试过设置ConcurrentLinkedQueue<Consumer<Collection<T>>> consumers = listeners.get(clazz);成为ConcurrentLinkedQueue<Consumer<Collection<? extends Event>>> consumers = listeners.get(clazz);但随后它提示 consumers.add(consumer);不是 Consumer<Collection<T>> 类型.

除了 consumers.add((Consumer<Collection<? extends Event>>)(Object)consumer); 之外,似乎没有任何转换可以解决这个问题。 ,但这对我来说看起来很可怕,而且似乎我一定错过了一些东西,或者搞砸了一些东西,因为这似乎不应该是一个必需的步骤。

最佳答案

您希望编译器知道键的类型是否为 Class<T> (其中 T extends Event ),则该值为 ConcurrentLinkedQueue<Consumer<Collection<T>>>

不幸的是,没有办法表达这个事实。但是,您知道这是真的,因为您放入映射中的唯一键/值对都具有这种形式。因此,加入 Actor 阵容是安全的,但我还会添加一条评论来解释为什么可以。

值的类型实际上应该是

ConcurrentLinkedQueue<? extends Consumer<? extends Collection<? extends Event>>>

不是

ConcurrentLinkedQueue<Consumer<Collection<? extends Event>>>

后者是 Consumers 的队列必须能够接受扩展 Event任何类型的集合,但你只想要 Consumer接受 Collections 某些特定类型 T延伸Event 。理解这一点非常困难,但请记住,您通常需要 ? extends当您将通配符与嵌套类型参数混合时,在每个级别上。

最后,eventToFire.getClass()不是您想要的,因为这返回某种集合类型的类,而您的键的类型为 Class<? extends Event> 。由于类型删除,您无法在运行时从集合中获取类型参数,因此您需要显式传递 clazzfire也是如此。

通过这些更改,代码变为

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

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

public <T extends Event> void fire(Class<T> clazz, Collection<T> eventToFire){
    ConcurrentLinkedQueue<Consumer<Collection<T>>> consumers = (ConcurrentLinkedQueue<Consumer<Collection<T>>>) listeners.get(clazz);
    if(consumers != null){
        consumers.forEach(x -> x.accept(eventToFire));
    }
}

唯一需要的转换是在假设键和值匹配的两行中,但这是您可以做的最好的事情。

关于java - 如何在带有通配符的泛型映射中使用带有类型化参数的泛型集合 (Java),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29504373/

相关文章:

java - 对于使用 Log4J(不是 SLF4J)的应用程序,如何将底层实现替换为我们内部开发的东西?

java - 看不到菜单栏

javascript - IE11 CSS Translate3d shifts DOM 元素事件

java - 如何理解 Java 泛型的这种用法

haskell - 这个 GHC.Generic 函数签名错误消息是什么意思?

java - getApplicationContext() 问题

c# - 在 C# 中,为什么我不能传递另一个类的 EventHandler 引用,我该如何绕过它?

javascript - 是否可以删除 javascript 中给定元素的所有事件处理程序?

java - Java 中的有界类型

java - 如何在 Java 中添加组件并将其置于 JFrame 的前面?