Variable inside thread is null at some point

I have an ArrayList that I am accessing in a Thread inside onStart method in JavaFX. When I am trying to access the ArrayList from another Thread again in a method that is triggered by a MenuItem it's null and I can't understand why.

Sample program:

package sample;

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

import java.util.ArrayList;

public class Main extends Application {

    private ArrayList<Integer> testArray;
    private Thread t;

    @Override
    public void start(Stage primaryStage) throws Exception{
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("sample.fxml"));
        fxmlLoader.setController(new Main());
        Parent root = (Parent)fxmlLoader.load();
        primaryStage.setTitle("Hello World");
        primaryStage.setScene(new Scene(root, 300, 275));
        primaryStage.show();

        testArray = new ArrayList<>();
        testArray.add(10);
        t = new Thread(() -> {
            System.out.println("Test number is "+testArray.get(0));
        });

        t.start();
    }

    @FXML
    public void menuItemActionMethod(ActionEvent event) {
        Thread t2t = new Thread(() -> {
            System.out.println("Test number is "+testArray.get(0));
        });

        t2t.start();
    }


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

sample.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>


<GridPane alignment="center" hgap="10" vgap="10" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8">
   <columnConstraints>
      <ColumnConstraints />
   </columnConstraints>
   <rowConstraints>
      <RowConstraints />
   </rowConstraints>
   <children>
      <Pane prefHeight="200.0" prefWidth="200.0">
         <children>
            <MenuBar>
              <menus>
                <Menu mnemonicParsing="false" text="File">
                  <items>
                    <MenuItem mnemonicParsing="false" text="Close" />
                  </items>
                </Menu>
                <Menu mnemonicParsing="false" text="Edit">
                  <items>
                    <MenuItem mnemonicParsing="false" onAction="#menuItemActionMethod" text="Delete" />
                  </items>
                </Menu>
                <Menu mnemonicParsing="false" text="Help">
                  <items>
                    <MenuItem mnemonicParsing="false" text="About" />
                  </items>
                </Menu>
              </menus>
            </MenuBar>
         </children>
      </Pane>
   </children>
</GridPane>

The output is the following:

Test number is 10
Exception in thread "Thread-5" java.lang.NullPointerException
    at sample.Main.lambda$menuItemActionMethod$1(Main.java:39)
    at java.lang.Thread.run(Thread.java:745)

I have attached menuItemActionMethod in FXML in Delete MenuItem. Running the sample and accessing Delete in the application should run the Method.

Jon Skeet
people
quotationmark

Now that we've finally got a repro, the problem is simply that menuItemActionMethod is being called on a different instance of Main to the start method.

Not being a JavaFX developer, I can't easily follow the code flow here, but you can demonstrate this pretty simply by removing the threading entirely:

private String name = "none set";

@Override
public void start(Stage primaryStage) throws Exception{
    name = "set in start";
    FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("sample.fxml"));
    fxmlLoader.setController(new Main());
    Parent root = (Parent)fxmlLoader.load();
    primaryStage.setTitle("Hello World");
    primaryStage.setScene(new Scene(root, 300, 275));
    primaryStage.show();
}

@FXML
public void menuItemActionMethod(ActionEvent event) {
    System.out.println("In menuItemActionMethod: " + name);
}

When you hit the menu item, you'll see:

In menuItemActionMethod: none set

Indicating that the call to menuItemActionMethod is made on a different object to the one that start is called on.

Next steps:

  • Read up more on the JavaFX object lifetime
  • Work out how to find the instance of Main that will be used in the event handler
  • Ideally, separate the initialization class from the event-handling class to make it harder to get into this situation to start with

people

See more on this question at Stackoverflow