ListenerList - a Better Way to Manage Your Event Listeners

You have published a listener interface and have a place where your clients register their listeners. There you typically code like this:

List<SomeEventListener> listeners = new ArrayList<SomeEventListener>(); 

public void addSomeEventListener(SomeEventListener listener) { 
    listeners.add(listener); 
} 

public void removeSomeEventListener(SomeEventListener listener) { 
    listeners.remove(listener); 
} 

public void fireSomeEvent(SomeEvent event) { 
    for (SomeEventListener listener : listeners) { 
        listener.eventOccured(event); 
    } 
}

The problem with this approach is two fold:

  1. Memory usage: The capacity of the ArrayList is always >= its size. So if you just have 200 listeners, there is a good chance that the memory might have been allocated for 300 elements
  2. Thread safety: Murphy's law will prove itself, and multi threaded bugs can be a pain in your back to debug. You have to properly sync all the write access and provide faster read only access for event firing operations.

To handle these two issues, Eclipse gives you a nice API: ListenerList class. Its meant for storing these kind of listeners, where any write modification to the underlying array (add, remove, clear) is synchronized and give access to the underlying array for faster read access. Memory is allocated only for the number of listeners registered.

The same above example can be rewritten as:

ListenerList listeners = new ListenerList(); 

public void addSomeEventListener(SomeEventListener listener) { 
    listeners.add(listener); 
} 

public void removeSomeEventListener(SomeEventListener listener) { 
    listeners.remove(listener); 
} 

public void fireSomeEvent(SomeEvent event) { 
    Object[] listenersArray = listeners.getListeners(); 
    for (int i = 0; i < listenersArray.length; i++) { 
        ((SomeEventListener)listenersArray[i]).eventOccured(event); 
    } 
}

 

ListenerList provides two ways of duplicate checking of the listeners: Equality and Identity. It can be specified in the constructor. Since the core Eclipse platform is in Java 1.4, you still can't use generics and foreach, but that shouldn't be a hindrance.

Question: Now that I do have a direct access to the underlying array, what if i manipulate it like this:

Object[] listenersArray = listeners.getListeners(); 
listenersArray[0] = null; 
listenersArray[1] = someEventListener5;

 

Well, the same thing happens when you shoot yourself in the foot :-)

 

From http://blog.eclipse-tips.com/
0

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Mikael Grev replied on Thu, 2008/12/18 - 2:28pm

With real properties in the language you would write something like this:

public property MyListenerProperty myListeners;

and that would be it... Optimized, done and 10% of the code. :)

Mario Cormier replied on Thu, 2008/12/18 - 2:57pm

Swing has its own variant (javax.swing.event.EventListenerList), which incidentally has nothing Swing-specific in it.  It could have been placed somewhere in java.beans or java.util (in which case more people would probably know about it and avoid building their own, often buggy solutions).  The big difference I see with Eclipse's version is that it is designed to contain listeners of different types.  This may reduce the footprint in some cases, but it also requires a little more boilerplate code, code that's just not trivial enough to remember all the time.  To address these issues, where I work we designed a subclass that uses a more functional style that hides all the ugly boilerplate under the covers.  The resulting code to fire an event looks like:

                        listeners.fireEvent(SelectionListener.class,
new EventListenerList.Callback() {

public void invoke(EventListener listener) {
((SelectionListener) listener).selected(ad);
}
});
 

Casper Bang replied on Thu, 2008/12/18 - 5:24pm in response to: mgrev

Yeah but in the conservative Java world, people love those 90% and think its somehow good for readability (confusing how-it-is-done with what-is-done). Properties are good for so many other things, lacking them is the sole reason why we currently need to express so many things out-side Java (EL, JPQL. beans-binding annotations etc.).

Mikael Grev replied on Sun, 2008/12/21 - 9:57am

It is important to note that one should always care for the situation where the listener removes itself or another listener. Normally you handle this by making a copy of the array before you start iterating the listeners. There are more advanced ways though...

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.