简体   繁体   English

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

[英]How to use generic collections with typed parameters in a generic map with wildcards (Java)

This question is related to How to create and fire a collection of Consumers<T> in java for an event listener pattern . 此问题与如何在Java中为事件侦听器模式创建并触发Consumers <T>的集合有关 I was trying to use a collection of events to help batch operations up, but so far I can't seem to come up with the correct configuration of generic definitions to work. 我试图使用一组事件来帮助进行批处理操作,但到目前为止,我似乎仍无法提出正确的通用定义配置。

Here is my code so far. 到目前为止,这是我的代码。

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

I've tried setting ConcurrentLinkedQueue<Consumer<Collection<T>>> consumers = listeners.get(clazz); 我试过设置ConcurrentLinkedQueue<Consumer<Collection<T>>> consumers = listeners.get(clazz); to be ConcurrentLinkedQueue<Consumer<Collection<? extends Event>>> consumers = listeners.get(clazz); 成为ConcurrentLinkedQueue<Consumer<Collection<? extends Event>>> consumers = listeners.get(clazz); ConcurrentLinkedQueue<Consumer<Collection<? extends Event>>> consumers = listeners.get(clazz); but then it complains that the consumers.add(consumer); 但随后它抱怨consumers.add(consumer); is not the type Consumer<Collection<T>> . 不是类型Consumer<Collection<T>>

No casting seems to be getting around this, with the exception of consumers.add((Consumer<Collection<? extends Event>>)(Object)consumer); 除了consumers.add((Consumer<Collection<? extends Event>>)(Object)consumer);之外,似乎没有任何强制措施可以解决这个问题consumers.add((Consumer<Collection<? extends Event>>)(Object)consumer); , but this just looks horrible to me, and it seems like I must be missing something, or messing something up, because it doesn't seem like this should be a required step. ,但这对我来说简直太可怕了,似乎我必须丢失某些东西,或者弄乱了一些东西,因为这似乎不是必需的步骤。

No, that's pretty much how it works. 不,这几乎就是它的工作原理。 Java's type system can't express the constraint you're trying to express. Java的类型系统无法表达您要表达的约束。 Your wrapper implementation is type-safe, but the compiler can't prove it, so you'll have to do dirty unsafe casts to convince the compiler you know what you're doing. 包装器的实现是类型安全的,但是编译器无法证明这一点,因此您必须进行肮脏的不安全的强制转换才能说服编译器您知道自己在做什么。

You want the compiler to know that if the key is of type Class<T> (where T extends Event ), then the value is a ConcurrentLinkedQueue<Consumer<Collection<T>>> . 您希望编译器知道,如果键的类型为Class<T>T extends Event ),则该值为ConcurrentLinkedQueue<Consumer<Collection<T>>>

Unfortunately, there is no way to express this fact. 不幸的是,没有办法表达这一事实。 However, you know it's true because the only key/value pairs you ever put into the map have that form. 但是,您知道这是真的,因为您放入地图中的唯一键/值对都具有该形式。 Therefore it's safe to put in the cast, but I would also include a comment explaining why it's ok. 因此,放个演员表是安全的,但我还要添加一条评论,解释为什么还可以。

The type of the values should actually be 值的类型实际上应该是

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

not

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

The latter is a queue of Consumers that must be able to accept a collection of any type extending Event , but you only want a Consumer that accepts Collections of some specific type T extending Event . 后者是Consumers队列,必须能够接受任何类型的扩展Event的集合,但是您只希望Consumer接受某种特定类型的 T扩展Event Collections It's incredibly difficult to understand this, but just remember that you usually need ? extends 这是令人难以置信的很难理解这一点,但只要记住,你通常需要? extends ? extends at every level when you mix wildcards with nested type parameters. 当您将通配符与嵌套类型参数混合使用时, ? extends到每个级别。

Finally, eventToFire.getClass() isn't what you want as this returns the class of some collection type, whereas your keys are of type Class<? extends Event> 最后, eventToFire.getClass()不是您想要的,因为它返回某种集合类型的类,而您的键的类型是Class<? extends Event> Class<? extends Event> . Class<? extends Event> Due to type erasure, you cannot get the type parameter from a collection at runtime, so you will need to explicitly pass clazz to fire as well. 由于类型擦除,您无法在运行时从集合中获取type参数,因此您还需要显式传递clazz才能fire

With these changes the code becomes 通过这些更改,代码变为

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

The only casts required are in the two lines that assume the key and value match, but this is the best you can possibly do. 唯一需要的强制转换是在假定键和值匹配的两行中进行的,但这是您可能要做的最好的转换。

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

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