Ошибка JavaFX - Исключение в потоке «Поток приложения JavaFX» java.lang.NullPointerException - PullRequest
0 голосов
/ 18 марта 2019

Я получаю это сообщение об ошибке, когда пытаюсь вызвать метод "отменить" в объекте вызова класса Invoker:

Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
    at view.MainWindow.lambda$2(MainWindow.java:112)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
    at javafx.event.Event.fireEvent(Event.java:198)
    at javafx.scene.control.MenuItem.fire(MenuItem.java:462)
    at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.doSelect(ContextMenuContent.java:1405)
    at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.lambda$createChildren$343(ContextMenuContent.java:1358)
    at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.event.Event.fireEvent(Event.java:198)
    at javafx.scene.Scene$MouseHandler.process(Scene.java:3757)
    at javafx.scene.Scene$MouseHandler.access$1500(Scene.java:3485)
    at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1762)
    at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2494)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:394)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$353(GlassViewEventHandler.java:432)
    at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:431)
    at com.sun.glass.ui.View.handleMouseEvent(View.java:555)
    at com.sun.glass.ui.View.notifyMouse(View.java:937)
    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(Unknown Source)

Я предполагаю, что проблема связана с потоком javafx? Я пытался использовать Platform.runLater, но проблема не исчезла.

Я запускаю окно JavaFX через это:

public static void main(String[] args) {
    Board board = new Board();
    Invoker invoker = new Invoker();
    TileBoardController tbc = new TileBoardController();
    MainWindow mainWindow = new MainWindow();
    com.sun.javafx.application.PlatformImpl.startup(()->{});
    mainWindow.launch(board, tbc.CreateTiles(board), invoker);
    com.sun.javafx.application.PlatformImpl.exit();
}

И событие, вызывающее сбой программы, находится в строке меню в этом классе "MainWindow":

menuOptions.setOnAction(e -> invoker.undo());

Странно то, что класс invoker работает и успешно используется в другой части того же объекта "MainWindow":

tileBoardStatic[rank][file].setOnAction( e -> this.onTileClick(finalRank, finalFile));

private void onTileClick(int rank, int file) {

        invoker.storeAndExecute(new MovePiece(board, activeTile.getSquare(), tileBoardStatic[rank][file].getSquare()));

}

Я пытался invoker.undo() тем же способом, и это работает. Кто-нибудь знает, в чем здесь проблема?

Вот весь код в MainWindow:

public class MainWindow extends Application{

    public static final int TILE_SIZE = 100;
    public static final int WIDTH = 8;
    public static final int HEIGHT = 8;

    private Invoker invoker;
    private static Group tileGroup = null;
    private static Tile[][] tileBoardStatic = null;
    private Tile activeTile;
    private Board board;


    public void launch(Board board, Tile[][] tileBoard, Invoker invoker) {
        this.board = board;
        this.tileBoardStatic = tileBoard;
        this.invoker = invoker;
        tileGroup = new Group();
        for (int rank=0; rank<tileBoard.length; rank++) {
            for(int file=0; file<tileBoard[rank].length; file++) {
                final int finalRank = rank;
                final int finalFile = file;
                tileBoardStatic[rank][file].setOnAction( e -> this.onTileClick(finalRank, finalFile));
                tileGroup.getChildren().add(tileBoard[rank][file]);
            }
        }
        Application.launch(MainWindow.class);
    }


    //Handles click event for Tiles
    private void onTileClick(int rank, int file) {


        if (activeTile == null) {
            if (tileBoardStatic[rank][file].getSquare().getPiece() == null) 
                System.out.println("No piece in tile");
            else {
                if(tileBoardStatic[rank][file].getSquare().getPiece().isWhite() != board.getTurnIsWhite()) {
                    System.out.println("Not your turn");
                } else {
                    activeTile = tileBoardStatic[rank][file];
                    activeTile.getStyleClass().add("tile-clicked");
                }
            }
        } 
        else {
            activeTile.getStyleClass().removeAll("tile-clicked");
            //Creates and sends command to invoker
            invoker.storeAndExecute(new MovePiece(board, activeTile.getSquare(), tileBoardStatic[rank][file].getSquare()));

            //Repaints squares
            activeTile.paintSquare();
            tileBoardStatic[rank][file].paintSquare();

            activeTile = null;
        }



    }


    @Override
    public void start(Stage primaryStage) throws Exception {
        BorderPane root = new BorderPane();
        primaryStage.getIcons().add(new Image(MainWindow.class.getResourceAsStream("resources/chess.png")));
        root.getChildren().addAll(tileGroup);
        root.setPrefSize(WIDTH * TILE_SIZE, HEIGHT * TILE_SIZE);

        Scene scene = new Scene(root);
        primaryStage.setTitle("MainWindow");
        primaryStage.setScene(scene);
        scene.getStylesheets().add(MainWindow.class.getResource("resources/stylesheet.css").toExternalForm());



        //Menu bar
        MenuBar menuBar = new MenuBar();
        Menu gameMenu = new Menu("Game");
        menuBar.getMenus().add(gameMenu);
        MenuItem menuExit = new MenuItem("Exit game");
        menuExit.setOnAction(e -> exit());
        gameMenu.getItems().add(menuExit);
        Menu menuOptions = new Menu("Edit");
        MenuItem menuUndo = new MenuItem("Undo");
        menuOptions.setOnAction(e -> invoker.undo());
        menuOptions.getItems().add(menuUndo);
        menuBar.getMenus().add(menuOptions);



        root.setTop(menuBar);
        primaryStage.show();

    }
    private void exit() {
        Platform.exit();
        System.exit(0);
    }

}

Ответы [ 2 ]

1 голос
/ 18 марта 2019

Вы неправильно запускаете приложение.Ваш метод main инициализирует некоторые объекты, включая экземпляр MainWindow.Затем вы вызываете метод для указанного экземпляра MainWindow, который устанавливает некоторые поля, затем вызывает Application.launch.Вот где проблема проявляется: вызов Application.launch приводит к созданию нового отдельного экземпляра MainWindow.Это этот экземпляр, который используется средой выполнения JavaFX.

Поскольку метод start(Stage) вызывается на экземпляре, отличном от того, на котором вы вызывали launch(Board,Tile[][],Invoker), ни один экземплярполя установлены правильно;это в конечном итоге приводит к вашему NullPointerException.Ваше решение сделать invoker статичным просто скрывает симптом более крупной проблемы - инициализации объектов вне типичного жизненного цикла.

Попробуйте изменить свой метод main на следующий:

public static void main(String[] args) {
    Application.launch(MainWindow.class, args);
}

Примечание. Если метод main находится внутри подкласса Application, вместо него можно использовать launch(args).

Затем переместить всю логику инициализации в эфир Application#init()или Application#start(Stage) (или, по крайней мере, вызовите соответствующий метод (методы) из одного из них).Любая инициализация, для которой требуется Поток приложения JavaFX , должна выполняться методом start;метод init вызывается в потоке JavaFX-Launcher .

Кроме того, использование Application.launch неявно запускает среду выполнения JavaFX.Там нет причин для вас, чтобы вручную позвонить PlatformImpl.startup.Вам также следует избегать использования внутреннего кода (например, com.sun.*), если нет других вариантов.Если вам нужно использовать внутренний код, сделайте шаг назад и проверьте, возможно, вы что-то делаете неправильно.

0 голосов
/ 18 марта 2019

Исправлена! Все, что мне нужно было сделать, это, конечно, установить статический объект invoker. Глупая мисс.

private static Invoker invoker;
...