javafx 8 compatibility issues - FXML static fields

Christopher_Daniel picture Christopher_Daniel · Apr 16, 2014 · Viewed 10.3k times · Source

I have designed a javafx application which works fine in jdk 7. When I try to run it in java 8 I am getting the below exceptions:

javafx.fxml.LoadException: 
at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2617)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2595)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2441)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3230)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3191)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3164)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3140)
    at javafx.fxml.FXMLLoader.load(FXMLLoader.java:3132)


Exception in thread "JavaFX Application Thread" java.lang.NullPointerException: Root cannot be null
    at javafx.scene.Scene.<init>(Scene.java:364)
    at javafx.scene.Scene.<init>(Scene.java:232)
        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.EventUtil.fireEventImpl(EventUtil.java:74)
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.event.Event.fireEvent(Event.java:204)
    at javafx.concurrent.EventHelper.fireEvent(EventHelper.java:219)
    at javafx.concurrent.Task.fireEvent(Task.java:1357)
    at javafx.concurrent.Task.setState(Task.java:720)
    at javafx.concurrent.Task$TaskCallable$2.run(Task.java:1438)
    at com.sun.javafx.application.PlatformImpl$6$1.run(PlatformImpl.java:301)
    at com.sun.javafx.application.PlatformImpl$6$1.run(PlatformImpl.java:298)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.application.PlatformImpl$6.run(PlatformImpl.java:298)
    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.access$300(WinApplication.java:39)
    at com.sun.glass.ui.win.WinApplication$4$1.run(WinApplication.java:112)
    at java.lang.Thread.run(Thread.java:744)

I found out the reason for this is in the initialize method of the controller class I am not able to use the inbuilt methods in any static component. (For example: staticMyTextField.setText() is causing the problem in java 8 but not in java 7). I am not able to find out anything documented regarding this in the javafx guides. Can someone please provide some ideas on why this is causing an issue in Java 8? And also share documents related to this if any.

Answer

James_D picture James_D · Apr 16, 2014

It sounds like you are trying to inject a TextField into a static field. Something like

@FXML
private static TextField myTextField ;

This apparently worked in JavaFX 2.2. It doesn't work in JavaFX 8. Since no official documentation ever supported this use, it's doesn't really violate backward compatibility, though in fairness the documentation on exactly what the FXMLLoader does is pretty woeful.

It doesn't really make much sense to make @FXML-injected fields static. When you load an FXML file, it creates new objects for each of the elements in the FXML file. A new controller instance is associated with each call to FXMLLoader.load(...) and the fields in that controller instance are injected with the corresponding objects created for the FXML elements. So the injected fields are necessarily specific to the controller instance. If you had a static injected fields in the controller, and you loaded the same FXML file twice and displayed it twice in the UI, then you would have no way of referencing both sets of controls.

Update: Response to question in comments

In particular, don't use static fields just to enable them to be accessible from outside the class. A static field has a single value belonging to the class, instead of a value for each instance of the class, and the decision to make fields static should only be made if that makes sense. In other words, static defines scope, not accessibility. To allow access to instance data, you just have to have a reference to the instance. The FXMLLoader has a getController() method that allows you to retrieve a reference to the controller.

A related point: it's also not a good idea to expose the UI controls from the controller. You should instead expose the data. For example, instead of defining a getTextField() method in the controller, instead define a textProperty() method that returns a StringProperty representing the contents of the TextField. The reason for this is that when your boss comes to the office and tells you he wants the TextField to be replaced by a TextArea, or a ComboBox<String>, or some other control, then it's going to be a lot harder if classes outside of the controller are using your TextField. The structure of the data represented by your controller is much less likely to change than the implementation of how that data is presented to the user.

For some examples