views:

273

answers:

4

I don't understand the rationale of this code, taken from javax.swing.event.EventListenerList docs:

protected void fireFooXXX() {
    // Guaranteed to return a non-null array
    Object[] listeners = listenerList.getListenerList();
    // Process the listeners last to first, notifying
    // those that are interested in this event
    for (int i = listeners.length-2; i>=0; i-=2) {
        if (listeners[i]==FooListener.class) {
            // Lazily create the event:
            if (fooEvent == null)
                fooEvent = new FooEvent(this);                 
            ((FooListener)listeners[i+1]).fooXXX(fooEvent);
        }
    }
}
  1. Why is the list traversed backwards?
  2. Why is only every second listener called?

The event firing is implemented exactly this way in javax.swing.tree.DefaultTreeModel among others, so it's obviously me who's just not getting something.

+2  A: 

to answer #2: Every second listener is called because the array that EventListenerList uses is set populated as an array of Listener-Type, Listener-Instance pairs.

akf
+3  A: 
  1. Probably performance considerations: backwards iteration is faster because comparison with 0 is a single machine code instruction - lots of former C programmers have that ingrained even though it's rather irrelevant nowadays. Note that there is no guarantee about the order in which listeners will be notified anyway.
  2. Look at the rest of the class - it stores the listeners' types as well to provide type safety.
Michael Borgwardt
Ah, ok. I just wonder why it isn't written like `FooListener[] listener = listenerList.getListeners(FooListener.class)`. Would be quite a bit cleaner, IMHO. Perhaps it has to do with performance as well.
Joonas Pulakka
I think the code example in the comment is outdated - the getListeners() method was added in Java 1.3
Michael Borgwardt
maybe they are doing it backwards because they want to notify the last listener first?
Suraj Chandran
+3  A: 

1.

The reason for traversing the listeners backwards is that it allows the listeners to remove themselves as listeners in their implementation of fooXXX().

Consider this listener, that might remove itself after receiving the event:

public class FooListener implements EventListener {
   private int i;

   public FooListener(int i) {
       this.i = i;
   }

   public fooXXX(FooEvent foo) {
       System.out.println(i);
       if (i == 1) {
           ((FooEventSource)foo.getSource()).removeListener(this);
       }
   }
} 

Now suppose we create a number of these listeners:

fooEventSource.addListener(new FooListener(0));
fooEventSource.addListener(new FooListener(1));
fooEventSource.addListener(new FooListener(2));
fooEventSource.addListener(new FooListener(3));

If we were to iterate forwards over the listeners we would get the following output:

0
1
3

We would be looping over the listeners by index, from 0 to 3. At index 1 the listener removes itself from the internal array of listeners, causing listeners 2 and 3 to be shifted down to index 1 and 2. The loop continues with index 2 which now contains listener 3. Listener 2 has been skipped.

By iterating backwards this problem is eliminated since removing a listener would only shift the index of listeners that have already been called.

2.

akf and Michael Borgwardt has already answered that the EvenListenerList stores the listener types in addition to the listeners. I guess the reason for this is that it makes it possible for a single EventListenerList to handle listeners of different types.

Johan Kaving
A: 

Johan Kaving is correct. The O'reilly Swing Hacks (#94) explains this in more detail.