I'm currently writing a simple Event Listener library.
Here's my interface for any listener:
public interface Listener<T> {
// Event is a very simple type which holds a variable T data.
public boolean run(Event<T> e);
}
My class Listenable
records all Listener
s in a HashMap:
protected HashMap<String, ArrayList<Listener<?>>> listeners;
I'm using a wildcard here because I want my Listenable
instances to have more than one event type.
The problematic part comes now in my Listenable::dispatchEvent()
method:
public boolean dispatchEvent(Event<?> evt) {
ArrayList<Listener<?>> evtListeners = listeners.get(evt.getType());
if (evtListeners == null) {
return true;
}
for (Listener<?> lst : evtListeners) {
// vvv--- error
if (!lst.run(evt) || evt.shouldStopPropagation()) {
return false;
}
}
return true;
}
The error message says:
The method run(Event) in the type Listener is not applicable for the arguments (Event)
I've found a "solution" (in the sense of getting the compiler to hide the error):
for (Listener lst : evtListeners) {
if (!lst.run(evt) || evt.shouldStopPropagation()) {
return false;
}
}
The compiler only generates two warnings in this case but I've read here that this technique is very, very poor!
I got it working with this code
public <T> boolean dispatchEvent(Event<T> evt) {
ArrayList<Listener<?>> evtListeners = listeners.get(evt.getType());
if (evtListeners == null) {
return true;
}
for (int i = 0; i < evtListeners.size(); i++) {
@SuppressWarnings("unchecked")
Listener<T> lst = (Listener<T>) evtListeners.get(i);
if (!lst.run(evt) || evt.shouldStopPropagation()) {
return false;
}
}
return true;
}
But I doubt that this is clean code, isn't it? I assume that the user of my library won't mix types for the same event type (evt.getType()
).
I would appreciate any suggestions!