通过其changeListener修改ObservableList引发UnsupportedOperationException

时间:2019-01-05 15:32:11

标签: java javafx observablelist unsupportedoperation

我当时正在做一个简单的基于井字游戏GUI的游戏。为此,我使用了2D ObservableList,其中填充了Figures枚举器。我的一举一动都会将所选单元格的值更改为X或O,然后检查是否有人获胜或游戏是否以平局结束。如果是这样,我用空白填充列表。至少这是应该起作用的方式。我遇到了标题中描述的问题。在任何情况下都不会发生此问题,我不明白这里会发生什么,但未能在Google上找到。这是我的代码的简化版本:(感谢帮助)

public class Example{
    enum Figures { NONE, X, O }
    static ObservableList<ObservableList<Figures>> list = FXCollections.observableArrayList();

    public static void main(String... args) {
        for (int i = 0; i < 3; i++) {
            ObservableList<Figures> subList = FXCollections.observableArrayList();
            list.add(subList);

            subList.addListener((ListChangeListener<Figures>) change -> {
                while (change.next()) {
                    Figures figure = subList.get(change.getFrom());
                    if (figure == Figures.NONE) {
                        //show Blank space
                    } else {
                        if (figure == Figures.X) {
                            //show X
                        }
                        if (figure == Figures.O) {
                            //show O
                        }

                        checkState();
                    }
                }
            });
            for (int j = 0; j < 3; j++) {
                subList.add(Figures.NONE);
            }
        }

        //it is a graphical game but here is what I do by clicking buttons to get the exception
        list.get(2).set(0, Figures.X);
        list.get(1).set(0, Figures.O);
        list.get(1).set(1, Figures.X);
        list.get(0).set(1, Figures.O);
        list.get(0).set(2, Figures.X);
    }

    private static void checkState() {
        //checking state and if win or draw
            restartGame();
    }

    private static void restartGame() {
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                list.get(i).set(j, Figures.NONE);
            }
        }
    }
}

和输出

Exception in thread "JavaFX Application Thread" java.lang.UnsupportedOperationException
at java.util.Collections$UnmodifiableList.add(Collections.java:1314)
at javafx.collections.ListChangeBuilder.nextRemove(ListChangeBuilder.java:208)
at javafx.collections.ListChangeBuilder.nextSet(ListChangeBuilder.java:453)
at javafx.collections.ObservableListBase.nextSet(ObservableListBase.java:115)
at javafx.collections.ModifiableObservableListBase.set(ModifiableObservableListBase.java:162)
at Main.restartGame(Main.java:239)
at Main.lambda$gameScene$2(Main.java:79)
at com.sun.javafx.collections.ListListenerHelper$SingleChange.fireValueChangedEvent(ListListenerHelper.java:164)
at com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(ListListenerHelper.java:73)
at javafx.collections.ObservableListBase.fireChange(ObservableListBase.java:233)
at javafx.collections.ListChangeBuilder.commit(ListChangeBuilder.java:482)
at javafx.collections.ListChangeBuilder.endChange(ListChangeBuilder.java:541)
at javafx.collections.ObservableListBase.endChange(ObservableListBase.java:205)
at javafx.collections.ModifiableObservableListBase.set(ModifiableObservableListBase.java:163)
at Main.gameScene(Main.java:170)
at Main.start(Main.java:27)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$161(LauncherImpl.java:863)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$174(PlatformImpl.java:326)
at com.sun.javafx.application.PlatformImpl.lambda$null$172(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$173(PlatformImpl.java:294)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$147(WinApplication.java:177)
at java.lang.Thread.run(Thread.java:748)
Exception in thread "JavaFX Application Thread" java.lang.UnsupportedOperationException
at java.util.Collections$UnmodifiableList.add(Collections.java:1314)
at javafx.collections.ListChangeBuilder.nextRemove(ListChangeBuilder.java:208)
at javafx.collections.ListChangeBuilder.nextSet(ListChangeBuilder.java:453)
at javafx.collections.ObservableListBase.nextSet(ObservableListBase.java:115)
at javafx.collections.ModifiableObservableListBase.set(ModifiableObservableListBase.java:162)
at Main.restartGame(Main.java:239)
at Main.lambda$gameScene$2(Main.java:79)
at com.sun.javafx.collections.ListListenerHelper$SingleChange.fireValueChangedEvent(ListListenerHelper.java:164)
at com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(ListListenerHelper.java:73)
at javafx.collections.ObservableListBase.fireChange(ObservableListBase.java:233)
at javafx.collections.ListChangeBuilder.commit(ListChangeBuilder.java:482)
at javafx.collections.ListChangeBuilder.endChange(ListChangeBuilder.java:541)
at javafx.collections.ObservableListBase.endChange(ObservableListBase.java:205)
at javafx.collections.ModifiableObservableListBase.set(ModifiableObservableListBase.java:163)
at Main.gameScene(Main.java:171)
at Main.start(Main.java:27)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$161(LauncherImpl.java:863)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$174(PlatformImpl.java:326)
at com.sun.javafx.application.PlatformImpl.lambda$null$172(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$173(PlatformImpl.java:294)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$147(WinApplication.java:177)
at java.lang.Thread.run(Thread.java:748)

1 个答案:

答案 0 :(得分:3)

问题是您的checkState()中的ListChangeListener函数。根据{{​​3}}

上的javadoc
  

警告:此类直接访问源列表以获取有关>更改的信息。   当列表上发生另一个更改时,这有效地使Change对象无效。   因此,在其他线程上使用此类并不安全。   这也意味着无法在侦听器内部修改源列表,因为这会使所有后续侦听器的Change对象失效。