Often times when developing in Swing, you need to implement your own events and listener API. A naive approach would be to simply use a basic collection:
public class SomeClass { private List listeners = new ArrayList(); public void addSomeClassListener(SomeClassListener l) { listeners.add(l); } public void removeSomeClassListener(SomeClassListener l) { listeners.remove(l); } protected void fireEvent() { SomeClassEvent evt = new SomeClassEvent(); for(Iterator iter = listeners.iterator(); iter.hasNext(); ) { SomeClassListener aListener = (SomeClassListener)iter.next(); aListener.eventHasOccurred(evt); } }
The biggest problem with this example is that it is simply not thread-safe. The listeners list can easily be changed by thread A at the same time that thread B is iterating the collection.
There is a set of three classes that have been around for a long long time in Java that are designed to solve the worst of these problems, and it's typically good practice to use them as opposed to a one-off home baked solution:
javax.swing.event.EventListenerList
- A thread-safe 'collection' for implementations of theEventListener
interface.java.util.EventListener
- A marker interface for a listener for some event.java.util.EventObject
- A helpful super class for the event object that includes a 'source' object reference.
Using these classes is fairly straight-forward:
public class SomeClassEvent extends EventObject { public SomeClassEvent(SomeClass source) { super(source); } // ... } public class SomeClassListener implements EventListener { void eventHasOccurred(SomeClassEvent evt); } public class SomeClass { private EventListenerList listeners = new EventListenerList(); public void addSomeClassListener(SomeClassListener l) { listeners.add(SomeClassListener.class, l); } public void removeSomeClassListener(SomeClassListener l) { listeners.remove(SomeClassListener.class, l); } public void fireEvent() { SomeClassEvent evt = new SomeClassEvent(this); // Java 5 inferred type SomeClassListener[] listenerArry = listeners.getListeners(SomeClassListener.class); for(int i=0; i<listenerArry.length; i++) { SomeClassListener listener = listenerArry[i]; listener.eventHasOccurred(evt); } } }
Now your code has some standard listener pattern in place, and your listener list is thread-safe. Note the use of the 'SomeClassListener.class' reference through-out the code. This is because the event listener list can contain events of varying types - when you call getListeners(Class<T>)
it filters the internal collection down to just listeners of that type. Note that while I think it is good enough to get the job done, I personally feel there are some implementation short-comings with the EventListenerList API - what do you think - do you have a better approach?
Until next time,
R.J. Lorimer Contributing Editor -
rj -at- javalobby.org Author -
http://www.coffee-bytes.com Software Consultant -
http://www.crosslogic.com