是否可以在不可修改的ObservableSet上注册SetChangeListener?

时间:2014-08-09 23:44:01

标签: java set listener observablecollection javafx-8

背景

在一个普通的老人" JavaFX桌面应用程序,我有一个实体,它公开ObservableSet的不可修改的视图,如下所示:

public ObservableSet<AbstractMessage> getMessagesUnmodifiable() {
    return FXCollections.unmodifiableObservableSet(messages);
}

在其他地方,客户端代码使用此API,如下所示:

model.getMessagesUnmodifiable()
        .addListener((SetChangeListener<AbstractMessage>) change -> {
            if (change.wasAdded())
                // ... do some work
        });

从第1天(今天实际上)我写了我的客户端代码后,我立刻感到有点担心。是否可以将监听器添加到不可修改的集?如果我添加一个监听器,我没有修改过Set吗?

理论

我没有找到关于此主题的文档。 FXCollections.unmodifiableObservableSet()的JavaDoc有一行可以说与此问题无关:

  

在提供的observable之上创建并返回不可修改的包装器   集。

堂兄FXCollections.unmodifiableObservableMap()还有更多话要说:

  

只对基础ObservableMap进行变异操作   向无法修改的观察员报告   实例。 这允许客户跟踪地图中的更改但不允许   修改它的能力

我在引用中标记为粗体的内容正是我想要实现的内容,但使用的是Set而不是Map。由于可以使用不可修改和可观察的地图,我认为它也必须对集合的工作方式相同。

实践

我的客户端代码不会抛出异常。所以就是这样。但它不起作用。我的听众从未被召唤过。它确实有效如果我只是为了测试,重写实体的getter方法,这样他就可以返回顶部没有糖的可修改的ObservableSet引用。因此,对FXCollections.unmodifiableObservableSet()的调用显然会以某种方式打破集合的行为。

我在一个单独的测试应用程序中快速编写了几行代码。但是,在我的测试应用程序(一个简单的public static void main)中,它就像一个魅力。我重写了我的测试应用程序和我的真实桌面应用程序,以便两者之间的代码完全相同具有以下可怕的结果:向不可修改的视图添加侦听器在我的桌面应用程序中不起作用,但是它在我的测试应用程序中没有任何缺陷。我无法解释这一发现。

1 个答案:

答案 0 :(得分:4)

FXCollections.unmodifiableObservableSet()返回的集合以神秘的方式工作。你可能认为你在后台集中添加了一个监听器,但实际上,你添加的监听器直接进入包装器,后者已将自己注册为监听器(WeakSetChangeListener)。支持集。

只要包装器活着并且踢,他的个人监听器就会从支持集中拾取事件并将这些事件分发给我在包装器中注册的监听器。但是因为我从未存储过对包装器的强引用,所以包装器被垃圾收集器拾取并处理掉它只是时间问题。我所有触发快乐的听众都加入了这次旅行。

这里还说明了我的小型测试应用程序和桌面应用程序之间的完全差异,尽管代码相同,但它们有两个完全不同的结果。我的测试应用程序从头到尾立即执行。我的桌面应用程序需要一段时间才能从监听器注册到实际事件。正是在那个时候,一个GC开始并让我搞砸了。

在我花时间阅读源代码后,理论上我必须得出结论,不可修改但可观察的Map的工作方式相同。所以吸取教训:保存对不可修改的包装器的引用或重写客户端的API,以便客户端可以将监听器直接传递给支持集(这是我选择的解决方案)。

有关弱听众如何在JavaFX中工作的更多阅读,请参阅我发布的另一个答案here