为什么fireFooXXX()中EventListenerList是向后遍历的?

2023-11-22

我不明白这段代码的基本原理,取自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. 为什么列表是向后遍历的?
  2. 为什么只有每第二个侦听器被调用一次?

事件触发正是以这种方式实现的javax.swing.tree.DefaultTreeModel其中,所以显然是我没有得到一些东西。


1.

遍历侦听器时可能出现的一个问题在摇摆技巧项目 #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 已被跳过。

通过向后迭代,这个问题就被消除了,因为删除监听器只会改变已经被调用的监听器的索引。

But

EventListenerList不存在这个问题,因为add()和remove()方法是写时复制的,并且建议用法中的侦听器遍历在循环之前对getListenerList()返回的侦听器列表实例进行操作。

有关它的更多讨论可以在这个线程,原因似乎可以归结为以下之一:

  • 表现

  • 事件排序(最后添加的侦听器将最先收到通知)


2.

akf 和 Michael Borgwardt 已经回答过,EvenListenerList 除了存储侦听器之外还存储侦听器类型。我想这样做的原因是它使得单个 EventListenerList 可以处理不同类型的侦听器。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么fireFooXXX()中EventListenerList是向后遍历的? 的相关文章

随机推荐