EventQueue, AWTEvent, AWTEventMultiCaster, and EventListener in the java.awt package. However, because I wanted to use events generally and not merely in the context of graphical UI operations, I ended
up writing a simple dispatcher of my own. In this article, I present the pre-generics version of this dispatcher and show
how the introduction of generics improved the mechanism's usability.I wanted an all-purpose dispatcher that would maintain listener lists for any type of event. The Swing tradition of writing
many little methods to the pattern addXxxxListener(XxxxListener l); troubled me. To register a listener for an event, I wanted, as a client of my API, to be able to write this:
ThingChangedListener listener = new ThingChangedListener() { /* implement stuff here */ };
dispatcher.register(ThingChangedEvent.class, listener);
This way, the code interested in the event is completely decoupled from the code that generates the event. In the example above, I want to know when "ThingChanged" happens, but I don't care who makes it happen.
Similarly, to fire an event, I wanted to write this:
dispatcher.fire(new ThingChangedEvent());
Now I present the implementation of Dispatcher—first, the pre-generics version, then the generics version. It will be satisfying to observe that client code (such as the
two examples above) will remain unaffected by the migration.
Dispatcher used to look like this:
class Dispatcher {
Map map;
void register(Class eventClass, Object listener) { ... }
void fire(Event event) { ... }
}
Notice that the register() method takes an Object as the second parameter. In other words, it knows nothing about the listener type. Dispatcher gets away with this because it expects Event objects to know how to dispatch themselves. Exactly how events dispatch themselves will become clearer soon.
The register() method adds its listener parameter to a list stored in a map keyed by the eventClass parameter, as follows:
void register(Class type, Object listener) {
List list = (List)map.get(type);
if (list == null) {
list = new ArrayList();
map.put(type, list);
}
list.add(listener);
}
The fire() method iterates through the list of registered listeners and asks Event to handle each listener by calling its dispatchTo() method. This way, Dispatcher and the listener types are completely decoupled; Dispatcher requires no knowledge about the listener interface. fire() looks like this:
void fire(Event event) {
List list = (List)map.get(event.getClass());
if (list == null) {
return;
}
for (Iterator i = list.iterator(); i.hasNext();) {
event.dispatchTo(i.next());
}
}
Here is the Event interface:
interface Event {
void dispatchTo(Object listener);
}
This interface is somewhat inspired by java.awt.ActiveEvent. It differs in that the dispatch method takes a parameter, the listener to which Event should dispatch itself. This parameter's type is Object; in other words, it lacks type information. An Event implementation casts its listener parameter to the desired type and then calls whatever method it wants on its listener. Here's an example:
Archived Discussions (Read only)