在Java FX中使用initialize()方法之前,如何让控制器访问主应用程序?

在Java FX中使用initialize()方法之前,如何让控制器访问主应用程序?,java,javafx,nullpointerexception,Java,Javafx,Nullpointerexception,在我的控制器类中,我想在initialize()方法中使用一个对象。这个对象(及其getter)是在主类中定义的。因此,当我授予控制器对主应用程序的访问权限时,已经太晚了,因为对象是在未初始化的情况下调用的。这将导致出现NullPointerException 这是调用.fxml及其控制器的方法: public void showConfigurationOverview() { try { // Load configuration overview.

在我的控制器类中,我想在
initialize()方法中使用一个对象。这个对象(及其getter)是在主类中定义的。因此,当我授予控制器对主应用程序的访问权限时,已经太晚了,因为对象是在未初始化的情况下调用的。这将导致出现
NullPointerException

这是调用
.fxml
及其控制器的方法:

public void showConfigurationOverview() {
    try {
        // Load configuration overview.
        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(MainApp.class.getResource("view/ConfigurationOverview.fxml"));
        AnchorPane configurationOverview = (AnchorPane) loader.load();

        // Set configuration overview into the center of root layout.
        rootLayout.setCenter(configurationOverview);

        // Give the controller access to the main app.
        ConfigurationOverviewController controller = loader.getController();
        controller.setMainApp(this);

    } catch (IOException e) {
        e.printStackTrace();
    }
}
如果无法提前访问,我如何找到解决方案?我应该在我的控制器类中做些什么吗

编辑: 谢谢你的回答。我尝试过麦克沃尔夫先生的解决方案,效果很好。然而,控制器类中我的
setMainApp
方法如下:

public void setMainApp(MainApp mainApp) {
    this.mainApp = mainApp;

    // Add observable list data to the table
    configurationTable.setItems(mainApp.getConfigurationData());
}
但我必须将函数放置在其他位置,以便将可观察列表数据添加到表中。我做对了吗

此外,我还想尝试Slaw(和Fabian)的解决方案,但我不明白您实际上在做什么,为什么要将
这个
传递给我的控制器类的构造函数。提前感谢
initialize()
我假设您指的是控制器中的
initialize()
方法,而不是
应用程序中的
init()
方法(如果我弄错了,请告诉我)。有两种方法可以做到这一点:

  • 不要在FXML文件中使用
    fx:controller
    。而是创建自己的实例并对其进行配置。然后在调用
    load()
    之前调用
    loader.setController(controller)

  • 使用控制器工厂通过。在这里,您仍然可以使用
    fx:controller
    ,并且实际上可以将
    这个
    传递到控制器类的构造函数中

  • 选项2的示例:

    如果知道控制器工厂将仅实例化
    控制器
    ,则可以删除
    if
    检查以及
    else
    中的所有内容

    编辑:选项2的备选示例

    费边在评论中提到了这一点。我把它贴在这里是因为阅读答案中的代码要比阅读注释容易得多

    loader.setControllerFactory(clazz -> {
        Object controller;
        try {
            controller = clazz.getConstructor().newInstance();
        } catch (ReflectiveOperationException ex) {
            throw new RuntimeException(ex);
        }
        if (controller instanceof BaseController) {
            ((BaseController) controller).setMainApp(this);
        }
        return controller;
    });
    
    其中,
    BaseController
    可以是某个接口或抽象类。这允许您避免检查需要特殊处理的每种类型


    fabian再次提到,您可以通过引入依赖注入框架(如AfterburnerFX或CDI(上下文和依赖注入))来进一步实现这一点。

    要做到这一点,您可以创建控制器的手动实例,在其中注入所需的对象。然后您只需提交
    fxmloader
    的实例

    但是,要执行此操作,必须从
    FXML
    文件中删除
    fx:controller
    语句

    public void showConfigurationOverview() {
        try {
            ConfigurationOverviewController controller = new ConfigurationOverviewController();
            controller.setMainApp(this);
    
            // Load configuration overview.
            FXMLLoader loader = new FXMLLoader();
            loader.setLocation(MainApp.class.getResource("view/ConfigurationOverview.fxml"));
            loader.setController(controller);
            AnchorPane configurationOverview = (AnchorPane) loader.load();
    
            // Set configuration overview into the center of root layout.
            rootLayout.setCenter(configurationOverview);
    
            // Give the controller access to the main app.
            //ConfigurationOverviewController controller = loader.getController();
            //controller.setMainApp(this);
    
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    

    另一个解决方案可以是下面的一个(即使前两个也可以)。从FMXL文件中删除
    fx:controller
    ,并按如下方式加载:

    public void showConfigurationOverview() {
        // Give the controller access to the main app.
        ConfigurationOverviewController controller = new ConfigurationOverviewController(this);
        root.setCenter(controller.getConfigurationOverview());
    }
    
    并按如下方式更改控制器:

    public class ConfigurationOverviewController {
    
        private AnchorPane configurationOverview;
    
        private MainApp mainApp;
    
        @FXML
        private TableView configurationTable;
    
        public ConfigurationOverviewController(MainApp pMain) {
            mainApp = pMain;
            FXMLLoader loader = new FXMLLoader(getClass().getResource("YOUR_FXML"));
            loader.setController(this);
            try {
                configurationOverview = (AnchorPane)loader.load();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        public AnchorPane getConfigurationOverview() {
            return configurationOverview;
        }
    
        @FXML
        private void initialize() {
            // Do your stuff with mainApp
            configurationTable.setItems(mainApp.getConfigurationData());
        }
    
    }
    
    编辑


    这实际上是来自的解决方案。

    我也有类似的问题,但我发现我的控制器将指向MainApp的链接设为空。这就是我制作link
    static
    的原因。在此之后,我没有得到任何
    NullPointerException
    。 也许这会有帮助

    public class LoginController {
    @FXML
    private Label passwordLabl;
    
    @FXML
    private TextField passwordTexF;
    
    public static WordLernApp wordLernApp;
    
    public LoginController() {
    
        LoginController.wordLernApp = null;
    
    }
    
    public void setWordLernApp(WordLernApp wordLernApp) {
        LoginController.wordLernApp = wordLernApp;
    }
    

    除了考虑所有需要特殊处理的类之外,您还可以使用接口或公共基类。这将允许您对需要访问应用程序类的多个类使用相同的逻辑<代码>对象控制器;尝试{controller=clazz.newInstance();}(ReflectiveOperationException ex){throw new RuntimeException(ex);}if(controller instanceof MyType)((MyType)controller.setMain(this);返回控制器使用依赖注入框架甚至可以更进一步…根据您的编辑编辑我的。更一般的是,如果您使用@mrmcwolf解决方案,只需添加
    configurationTable.setItems(mainApp.getConfigurationData())
    在控制器的初始化方法中
    ConfigurationOverviewController
    。谢谢,我也这么做了。然而,在@mrmcwolf的解决方案中,我有一份来自Intellij的报告指出,
    initialize()
    方法现在从未使用过。这是正常的还是只是IDE的一个错误?因为应用程序似乎工作正常。您添加了@FXML注释吗?是的,当然。你可以从这里查到
    public class LoginController {
    @FXML
    private Label passwordLabl;
    
    @FXML
    private TextField passwordTexF;
    
    public static WordLernApp wordLernApp;
    
    public LoginController() {
    
        LoginController.wordLernApp = null;
    
    }
    
    public void setWordLernApp(WordLernApp wordLernApp) {
        LoginController.wordLernApp = wordLernApp;
    }