NullPointerException возникает после обновления элементов ComboBox - PullRequest
0 голосов
/ 08 марта 2020

Я застрял в этой проблеме и понятия не имею, как действовать.

У меня есть настройка приложения JavaFX в схеме MVC. У меня есть мои модели Game и Achievement и соответствующие им классы Controller и View, GameController, AchievementController, GameView, AchievementView. У меня также есть DAO для Game и Achievement.

Теперь в моей функции refreshData() в AchievementController я получаю следующий след ошибок:

https://pastebin.com/4TWZ6kjw

Я не понимаю, почему это происходит.

Описание шагов:

При открытии игры и переключении сцен с GameView на AchievementView Я вызываю функцию refreshData() в `

AchievementController.onSwitchView()

и работает нормально. Затем, после того, как я добавил новое достижение с помощью метода

AchievementController.handleAddButton()

, он бросает вышеупомянутый след, вызванный строкой в refreshData(), которая говорит

view.getGamePicker().setItems(gameList).

Я не понимаю, почему это происходит. Я проверил с помощью отладчика, и view, view.getGamePicker() и gameList не равны нулю. gameList - это список, который содержит точные объекты, которые он должен содержать, но они также не равны нулю.

Я знаю, что это на самом деле не минимальный воспроизводимый пример, но я действительно не знаю, как точно определить линии, которые вам действительно нужны. Я полностью в тупике! Если вам нужен весь проект, чтобы вы могли запустить его локально, проверьте здесь: https://github.com/kemmel-dev/TestRepo

Ссылки на (я думаю) соответствующие классы:

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

Ответы [ 2 ]

0 голосов
/ 09 марта 2020

TL; DR: Не устанавливайте для свойства ComboBox#items значение ObservableList, поддерживаемое List, которое не допускает 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, и вы действительно подтверждаете это в своем собственном ответе . Характеристики этих неизменяемых списков включают следующее:

Они запрещают null элементы [выделение добавлено] . Попытки создать их с элементами null приводят к NullPointerException.

И документации List#indexOf(E), которую вы видите, было вызвано просмотром трассировки стека , состояния:

Броски:

NullPointerException - если указанный элемент является нулевым и этот список не допускает нулевые элементы ( необязательно )

Затем, согласно ваш ответ снова, вы используете FXCollections#observableList(List). Этот метод:

Создает ObservableList, для которого поддерживается указанным списком [выделение добавлено] . Об операциях мутации в экземпляре ObservableList будет сообщено наблюдателям, которые зарегистрировались в этом экземпляре.

Это означает, что вы установили для элементов ComboBox значение ObservableList, поддерживаемое List, который не допускает null элементов. Если вы снова посмотрите на трассировку стека, вы увидите, что здесь происходит вызов #indexOf(E):

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

Вот исходный код (JavaFX 13.0.2) ComboBox вокруг этой строки:

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 элементов. Ваш ответ означает, что вы нашли хотя бы одно решение. Если вы хотите продолжить возвращать неизменяемый, недопустимый список из вашего DAO, и вы не против скопировать список, возвращенный вашим DAO, в другой список, тогда вы можете использовать FXCollections#observableArrayList(Collection), когда установить элементы вашего ComboBox или даже просто сделать:

comboBox.getItems().setAll(collectionFromDao);
0 голосов
/ 08 марта 2020

Я до сих пор не совсем уверен, почему, но использование List.copyOf() в моих DAO s вызывало эту проблему. Очевидно, setItems() метод ComboBox ИЛИ FXCollections.ObservableList() метод не очень хорошо подходит для получения List.copyOf() в качестве аргумента.

Замена его на Collections.unmodifiableList или new ArrayList<>(objects) сработало .. .

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