更新 ComboBox 的项目后发生 NullPointerException

Posted

技术标签:

【中文标题】更新 ComboBox 的项目后发生 NullPointerException【英文标题】:NullPointerException occurs after updating the items of a ComboBox 【发布时间】:2020-06-20 19:18:11 【问题描述】:

我被这个问题困住了,我不知道如何继续。

我在 MVC 方案中设置了 JavaFX 应用程序。我有我的模型GameAchievement 以及它们各自的ControllerView 类,GameControllerAchievementControllerGameViewAchievementView。我还有一个DAO 用于GameAchievement

现在在 AchievementController 中的函数 refreshData() 中,我得到以下错误痕迹:

https://pastebin.com/4TWZ6kjw

我不明白为什么会这样。

步骤说明:

当打开游戏并将场景从GameView 切换到AchievementView 时,我在`中调用refreshData() 函数

AchievementController.onSwitchView()

它运行良好。然后在我通过

添加新成就之后

AchievementController.handleAddButton()

方法,它抛出上面的跟踪,由the line 中的refreshData() 触发,上面写着

view.getGamePicker().setItems(gameList)

我不明白为什么会这样。我用调试器检查过,viewview.getGamePicker()gameList 都是非空的。 gameList 是一个列表,其中包含它应该包含的确切对象,这些对象也不为空。

我知道这并不是一个真正的最小可重现示例,但我真的不知道如何确定您实际需要的线条。我完全被难住了!如果您需要整个项目以便在本地运行它,请在此处查看:https://github.com/kemmel-dev/TestRepo

(我认为)相关课程的链接:

MainApplication DAO AchievementDAO GameDAO AchievementController GameController

您可以通过在启动后在第一个屏幕中添加一个或多个游戏来重现此行为,然后打开您刚刚添加的一个游戏,并尝试向该游戏添加一个成就。

【问题讨论】:

如果您阅读有关如何创建minimal reproducible example 的文章,它将帮助您隔离问题并创建需要包含在问题中的代码。请不要为您的代码链接到外部资源。 堆栈跟踪似乎表明在 ObservableList 或传递给 ObservableList 中有一个空引用。要么你真的在里面放了一些空值,要么存在线程问题。如前所述,您需要(可能从头开始)创建一个重现问题的最小示例,并在问题中包含来自您的最小示例的代码和堆栈跟踪。 @Zephyr 我的问题很可能不会出现在最小的可重现示例中。我认为我在滥用 API 并监督发生这种情况的地方。 这个论坛不是真的。如果您想要这种级别的支持,您需要支付顾问费用。 关于克隆项目的一件事,如果我们很聪明,我们仍然会先查看您的代码。我没有在我的机器上盲目运行大量未知代码的习惯:) 【参考方案1】:

我仍然不完全确定为什么,但是在我的 DAOs 中使用 List.copyOf() 导致了这个问题。显然,ComboBoxFXCollections.ObservableList() 方法的 setItems() 方法不能很好地接收 List.copyOf() 作为它的参数。

将其替换为 Collections.unmodifiableListnew ArrayList<>(objects) 有效...

【讨论】:

【参考方案2】:

TL;DR:不要将ComboBox#items 属性设置为由List 支持的ObservableList,它不允许null 元素。


堆栈跟踪的重要部分是前几帧(或最后几帧,取决于你如何看待它):

Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
    at java.base/java.util.Objects.requireNonNull(Objects.java:222)
    at java.base/java.util.ImmutableCollections$AbstractImmutableList.indexOf(ImmutableCollections.java:166)
    at javafx.base/com.sun.javafx.collections.ObservableListWrapper.indexOf(ObservableListWrapper.java:124)
    at javafx.controls/javafx.scene.control.ComboBox.lambda$new$1(ComboBox.java:245)

具体来说,这个框架:

at java.base/java.util.ImmutableCollections$AbstractImmutableList.indexOf(ImmutableCollections.java:166)

这意味着您正在使用在 Java 9/10 中添加的 unmodifiable lists 之一,并且您确实在 your own answer 中确认了这一点。这些不可修改列表的特点包括:

他们不允许 null 元素 [强调添加]。尝试使用 null 元素创建它们会导致 NullPointerException

您可以看到List#indexOf(E) 的文档是通过查看堆栈跟踪调用的,它指出:

投掷:

NullPointerException - 如果指定元素为空且此列表不允许空元素 (optional)

然后,再次根据your answer,你使用FXCollections#observableList(List)。那个方法:

构造一个由指定列表支持的ObservableList [强调添加]ObservableList 实例上的变异操作将报告给已在该实例上注册的观察者。

这意味着您已将 ComboBox 的项目设置为由 List 支持的 ObservableList,而 List 不允许使用 null 元素。如果您再次查看堆栈跟踪,您可以看到对 #indexOf(E) 的调用发生在此处:

at javafx.controls/javafx.scene.control.ComboBox.lambda$new$1(ComboBox.java:245)

下面是ComboBox 的源代码(JavaFX 13.0.2):

public ComboBox(ObservableList<T> items) 
    getStyleClass().add(DEFAULT_STYLE_CLASS);
    setAccessibleRole(AccessibleRole.COMBO_BOX);
    setItems(items);
    setSelectionModel(new ComboBoxSelectionModel<T>(this));

    // listen to the value property input by the user, and if the value is
    // set to something that exists in the items list, we should update the
    // selection model to indicate that this is the selected item
    valueProperty().addListener((ov, t, t1) -> 
        if (getItems() == null) return;

        SelectionModel<T> sm = getSelectionModel();
        int index = getItems().indexOf(t1); // LINE 245 (NPE THROWN HERE)

    // REST OMITTED FOR BREVITY

ComboBox 的值为null 是完全合法的。这可能导致t1 成为null,因此第 245 行变为:

int index = getItems().indexOf(null);

然后你的NullPointerException 被抛出。

解决方案是确保项目的ObservableList 允许null 元素。 Your answer 表示您至少找到了一种解决方案。如果您想继续从您的 DAO 返回一个不可变的、不允许空值的列表,并且您不介意将您的 DAO 返回的列表复制到另一个列表中,那么您可以在设置 @ 的项目时使用 FXCollections#observableArrayList(Collection) 987654360@ 或者干脆做:

comboBox.getItems().setAll(collectionFromDao);

【讨论】:

以上是关于更新 ComboBox 的项目后发生 NullPointerException的主要内容,如果未能解决你的问题,请参考以下文章

ExtJS Combobox Rowediting在单击更新后不显示最新值

WPF comboBox的SelectionChanged事件

WPF MVVM ComboBox SelectedItem 或 SelectedValue 不起作用

ComboBox:如果操作员键入文本,然后按回车,则发生哪个事件

DataSource 更改时,ComboBox 不会自动更新?

当 DataSource 值更改时,WinForms ComboBox 中的项目不会更新