Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/322.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 侦听器应该能够删除侦听器吗?_Java - Fatal编程技术网

Java 侦听器应该能够删除侦听器吗?

Java 侦听器应该能够删除侦听器吗?,java,Java,许多类使用类似于以下的代码来激发侦听器 private List<Listener> listeners = new ArrayList<Listener>(); public void fireListener() { for(Listener l : listeners) l.someMethod(); } 您可以先复制一份侦听器集合,以避免可能的ConcurrentModificationException: public void fireListener

许多类使用类似于以下的代码来激发侦听器

private List<Listener> listeners = new ArrayList<Listener>();

public void fireListener() {
  for(Listener l : listeners) l.someMethod();
}

您可以先复制一份
侦听器
集合,以避免可能的
ConcurrentModificationException

public void fireListener() {
  Listener[] ll = listeners.toArray(new Listener[listeners.size()]);
  for(Listener l : ll) l.someMethod();
}

无法向正在使用的列表/映射添加/删除。在C#中有一个很好的小类,叫做ConcurrentList/ConcurrentDictionary

在Java中,这是可能的,这里有一个很好的链接:


由于集合在循环中使用,因此该集合将在特定时间内使用。如果在循环内调用的函数中添加/删除侦听器,将出现一个错误,因为在没有并发的情况下不允许添加/删除侦听器。

在浏览侦听器时处理添加/删除侦听器的正确方法是使用支持此操作的迭代器

例如:

// Assuming the following:
final List<Listener> listeners = ...

final Iterator<Listener> i = listeners.iterator();
while (i.hasNext()) {
   // Must be called before you can call i.remove()
   final Listener listener = i.next(); 

   // Fire an event to the listener
   i.notify();

   if (/* true iff listener wants to be deregistered */) {
       i.remove(); // that's where the magic happens :)
   }
}
//假设如下:
最终列表侦听器=。。。
final Iterator i=listeners.Iterator();
while(i.hasNext()){
//在调用i.remove()之前必须先调用
最终侦听器=i.next();
//向侦听器触发事件
i、 通知();
如果(/*true iff侦听器希望取消注册*/){
i、 remove();//这就是神奇发生的地方:)
}
}
NB:请注意,某些
迭代器
不支持
迭代器#remove
方法


我想说,允许侦听器添加/删除其他侦听器(或自己)不是一个好主意。这表明关注点分离不良。毕竟,为什么听者应该知道关于调用者的任何事情?您将如何测试这样一个紧密耦合的系统

相反,您可以让事件处理方法(在侦听器中)返回一个布尔标志,指示侦听器不希望接收更多事件。这使得事件调度器有责任进行删除,并且它涵盖了大多数需要从侦听器中修改侦听器列表的用例

这种方法的主要区别在于,监听器只是简单地说了一些关于自己的事情(即“我不想要更多的事件”),而不是与调度器的实现联系在一起。这种解耦提高了可测试性,并且不会向另一个类公开任何一个类的内部

public interface FooListener {
    /**
     * @return False if listener doesn't want to receive further events.
     */
    public boolean handleEvent(FooEvent event);
}

public class Dispatcher {

    private final List<FooListener> listeners;

    public void dispatch(FooEvent event) {
      Iterator<FooListener> it = listeners.iterator();
      while (it.hasNext()) {
        if (!it.next().handleEvent(event))
          it.remove();
      }
    }
}
公共接口傻瓜监听器{
/**
*@return False,如果侦听器不想接收更多事件。
*/
公共布尔handleEvent(FooEvent事件);
}
公共类调度器{
私人最终听众名单;
公共无效调度(FooEvent事件){
Iterator it=listeners.Iterator();
while(it.hasNext()){
如果(!it.next().handleEvent(事件))
it.remove();
}
}
}
更新:在监听器中添加和删除其他监听器的问题稍微多一些(并且应该引起更大的警钟),但您可以遵循类似的模式:监听器应该返回关于需要添加/删除哪些其他监听器的信息,调度器应该根据该信息采取行动

但是,在这种情况下,您会遇到很多边缘情况:

  • 当前事件会发生什么
  • 是否应将其发送给新添加的侦听器
  • 是否应将其发送给即将移除的对象
  • 如果您试图删除列表中较早的内容,并且事件已发送到该内容,该怎么办
  • 另外,如果侦听器X添加了侦听器Y,然后删除了侦听器X,该怎么办?你应该去吗
所有这些问题都来自监听器模式本身,以及列表中所有监听器相互独立的基本假设。处理这些情况的逻辑应该在调度器中,而不是在侦听器中

更新2:在我的示例中,为了简洁起见,我使用了一个裸布尔值,但在实际代码中,我会定义一个两值枚举类型,以使契约更加明确。

有三种情况:

  • 您不希望在侦听器执行期间允许修改侦听器集合:
    在这种情况下,
    ConcurrentModificationException
    是合适的

  • 您希望允许修改侦听器,但更改不会反映在当前运行中:
    您必须确保对
    侦听器的修改不会对当前运行产生影响。一个
    CopyOnWriteArrayList
    就可以做到这一点。在使用它之前,请阅读API,其中有一些陷阱。
    另一个解决方案是在遍历列表之前复制它

  • 您希望对侦听器的更改反映在当前运行中:
    最棘手的案子。在这里使用for-each循环和迭代器不起作用(正如您所注意到的;-)。您必须自己跟踪更改。
    一个可行的解决方案是将侦听器存储在
    ArrayList
    中,并使用标准for循环遍历该列表:

    for (int i =0; i < listeners.size(); i++) { 
        Listener l = listeners.get(i);
        if (l == null) 
            continue;
        l.handleEvent();
    }  
    
    for(inti=0;i
    删除侦听器会将数组中的元素设置为null。 新的侦听器被添加到末尾,因此将在当前执行中运行。
    请注意,此解决方案只是一个示例,不是线程安全的! 有时可能需要一些维护来删除
    null
    元素,以防止列表变得太大

  • 你自己决定需要什么


    我个人最喜欢的是第二个。它允许在执行时进行修改,但不会更改当前运行的行为,这可能会导致意外的结果。

    请发布一个更详细的问题,其中包含更大的代码片段,以澄清问题。
    侦听器不是某种集合(如列表)吗?如果是这样的话,也许我没有回答这个问题,但一般来说
    
    for (int i =0; i < listeners.size(); i++) { 
        Listener l = listeners.get(i);
        if (l == null) 
            continue;
        l.handleEvent();
    }