javajavafxmodel-view-controllerinvocationtargetexception

I'm getting an java.lang.reflect.InvocationTargetException exception in JavaFX app and I have no idea how to fix it


I'm building a small D&D side project, but having trouble navigating my way through JavaFX. I'm attempting to move through several scenes while remaining on the same stage. I'm using an MVC design and passing though the primaryStage whenever the controller is called. However, when I try to call from my UI class and get the stage from my controller, it throws a InvocationTargetException.

Main Class:

package dnd;

import javafx.application.Application;
import javafx.stage.Stage;

public class DnD extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception { 
        primaryStage.setTitle("Dungeons and Dragons");
        StartMenuCtrl startMenuCtrl = new StartMenuCtrl(primaryStage);
        primaryStage.show();        
    }

    public static void main(String[] args) {
        launch(args);
    }   
}

Start Menu Controller Class:

package dnd;

import javafx.stage.Stage;

public class StartMenuCtrl {
    private final StartMenuUI startMenuUI;
    private final Stage primaryStage;

    public StartMenuCtrl(Stage primaryStage) {
        this.startMenuUI = new StartMenuUI(this);
        this.primaryStage = new Stage();        
    }
    
    public void newButtonIsPressed() {
        System.out.println("New Button is pressed");
    }
    
    public void createButtonIsPressed() {
        System.out.println("Create Button is pressed");
        CreateCharCtrl createCharCtrl = new CreateCharCtrl(primaryStage);
    }
    
    public void loadButtonIsPressed() {
        System.out.println("Load Button is pressed");
    }
    
    public void joinButtonIsPressed() {
        System.out.println("Join Button is pressed");
    }
    
    public Stage getStage() {
        return this.primaryStage;
    }
}

Start Menu UI Class:

package dnd;

import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;

public class StartMenuUI {
    private final StartMenuCtrl startMenuCtrl;
    private final Text title;
    private final Button newBtn;
    private final Button createBtn;
    private final Button loadBtn;
    private final Button joinBtn;

    public StartMenuUI(StartMenuCtrl startMenuCtrl) {
        this.startMenuCtrl = startMenuCtrl;
        this.title = new Text();
        this.newBtn = new Button("New Game");
        this.createBtn = new Button("Create Character");
        this.loadBtn = new Button("Load Saved Game");
        this.joinBtn = new Button("Join Game");
        initComponents();
    }
    
    private void initComponents() {
        Group root = new Group(title, newBtn, createBtn, loadBtn, joinBtn);
        Scene scene = new Scene(root, 1280, 720);
        Stage stage = startMenuCtrl.getStage();
        stage.setScene(scene);
    }
}

Error:

Exception in Application start method
java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:389)
    at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:328)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:767)
Caused by: java.lang.RuntimeException: Exception in Application start method
    at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:917)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$154(LauncherImpl.java:182)
    at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.NullPointerException
    at dnd.StartMenuUI.initComponents(StartMenuUI.java:70)
    at dnd.StartMenuUI.<init>(StartMenuUI.java:25)
    at dnd.StartMenuCtrl.<init>(StartMenuCtrl.java:10)
    at dnd.DnD.start(DnD.java:11)
    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)
    ... 1 more

Any help would be appreciated! I'm very early in the project so drastic change tips would be appreciated as well :)


Solution

  • You've got an initialization order issue. In your StartMenuCtrl constructor, you're passing a reference to this to the StartMenuUI constructor. The StartMenuUi constructor then calls initComponents, which calls startMenuCtrl.getStage()... but that method returns null because the StartMenuCtrl constructor has not finished executing, and has not yet initialized its primaryStage field.

    Try reordering the lines in the StartMenuCtrl constructor so they're in this order:

    this.primaryStage = new Stage(); // this needs to happen first!
    this.startMenuUI = new StartMenuUI(this);
    

    This problem illustrates why, generally speaking, it's best not to let the this reference escape during object construction.