I'm trying to build a Java event emitter, which would have a list of callbacks (implementing Consumer interface) mapped with an event name.
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;
}
}
I get this compile error:
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
but the captured type is clearly a subtype of , so it should work (but I understand I'm missing something).
The use should be something like this (where OpenEvent and CloseEvent extend EventObject of course):
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());
I suppose it's possible to do this type-safe, since I can specify the type of the consumer's object via lambda function. But how?
That happens because listener is of type: Consumer<? extends EventObject> (so, it's a Consumer of some specific, but unknown type that extends EventObject), but you want it to accept an event of type E. The compiler cannot check if the unknown type indicated by the wildcard is equal to the type E.
Why are you using wildcards at all? It would be better to get rid of them, and do something like this:
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;
}
}
Note: A wildcard type with a ? extends EventObject does not mean you can pass any object to it that extends EventObject; it specifies one specific, but unknown type that extends EventObject. Because what the exact type is, is unknown, this limits what you can do with it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With