Issue
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.
Answered By - dnault
Answer Checked By - Pedro (JavaFixing Volunteer)