Почему EventListenerList перемещается в обратном порядке в fireFooXXX ()? - PullRequest
6 голосов
/ 13 октября 2009

Я не понимаю обоснование этого кода, взятое из javax.swing.event.EventListenerList документы:

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. Почему список перемещается назад?
  2. Почему вызывается только каждый второй слушатель?

Запуск событий реализован именно таким образом в javax.swing.tree.DefaultTreeModel среди других, так что, очевидно, это я, кто просто ничего не получает.

Ответы [ 3 ]

6 голосов
/ 26 мая 2010

1.

Одна возможная проблема при обходе слушателей описана в Swing Hacks item # 94 и возникает, если один из них удаляет себя в качестве слушателя в реализации fooXXX ().

Рассмотрим этот слушатель, который может удалить себя после получения события:

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);
       }
   }
} 

и эта реализация обхода слушателя:

public void fireFooXXX() {
   for (int i=0; i<listeners.size(); i++) {
      // Lazily create the event:
      if (fooEvent == null)
         fooEvent = new FooEvent(this);
      listeners.get(i).fooXXX(fooEvent);
   }
}

Теперь предположим, что мы создали ряд таких слушателей:

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

Запуск события даст следующий результат:

0
1
3

Мы бы зациклились на слушателях по индексу, от 0 до 3. При индексе 1 слушатель удаляет себя из внутреннего массива слушателей, в результате чего слушатели 2 и 3 смещаются вниз к индексу 1 и 2. Цикл продолжается с индексом 2, который теперь содержит прослушиватель 3. Прослушиватель 2 был пропущен.

Путем итерации в обратном направлении эта проблема устраняется, поскольку удаление слушателя только сместит индекс прослушивателей, которые уже были вызваны.

Но

EventListenerList не имеет этой проблемы, поскольку методы add () и remove () являются копируемыми при записи, а обход слушателя в предлагаемом использовании работает с экземпляром списка слушателей, возвращаемым getListenerList () перед циклом.

Еще несколько обсуждений об этом можно найти в этой теме , где причины, по-видимому, сводятся к одной из:

  • производительность

  • порядок событий (последний добавленный слушатель будет уведомлен первым)


2

akf и Michael Borgwardt уже ответили, что EvenListenerList хранит типы слушателей в дополнение к слушателям. Я предполагаю, что причина этого в том, что он позволяет одному EventListenerList обрабатывать прослушиватели разных типов.

5 голосов
/ 13 октября 2009
  1. Вероятно, из соображений производительности: итерация в обратном направлении быстрее, потому что сравнение с 0 - это одиночная инструкция машинного кода - у многих бывших программистов на С это укоренилось, хотя в наши дни это довольно неактуально. Обратите внимание, что нет никакой гарантии относительно порядка, в котором слушатели будут уведомлены так или иначе.
  2. Посмотрите на остальную часть класса - он также хранит типы слушателей для обеспечения безопасности типов.
3 голосов
/ 13 октября 2009

, чтобы ответить # 2: каждый второй слушатель вызывается, потому что массив, который использует EventListenerList, устанавливается как массив пар типа слушатель, слушатель-экземпляр.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...