Problemas de compatibilidad con javafx 8 – Campos estáticos de FXML

He diseñado una aplicación javafx que funciona bien en jdk 7. Cuando bash ejecutarla en java 8 obtengo las siguientes excepciones:

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.(Scene.java:364) at javafx.scene.Scene.(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) 

Descubrí que la razón para esto está en el método de inicialización de la clase de controlador. No puedo usar los métodos incorporados en ningún componente estático. (Por ejemplo: staticMyTextField.setText () está causando el problema en java 8 pero no en java 7). No puedo encontrar nada documentado al respecto en las guías de javafx. ¿Puede alguien proporcionar algunas ideas sobre por qué esto está causando un problema en Java 8? Y también comparta documentos relacionados con esto, si corresponde.

Parece que estás intentando inyectar un TextField en un campo estático. Algo como

 @FXML private static TextField myTextField ; 

Esto aparentemente funcionó en JavaFX 2.2. No funciona en JavaFX 8. Dado que ninguna documentación oficial alguna vez admitió este uso, en realidad no viola la compatibilidad con versiones anteriores, aunque para ser justos, la documentación sobre exactamente lo que hace FXMLLoader es bastante lamentable.

En realidad, no tiene mucho sentido hacer que los campos inyectados con @FXML sean estáticos. Cuando carga un archivo FXML, crea nuevos objetos para cada uno de los elementos en el archivo FXML. Una nueva instancia de controlador se asocia con cada llamada a FXMLLoader.load(...) y los campos en esa instancia de controlador se inyectan con los objetos correspondientes creados para los elementos de FXML. Entonces los campos inyectados son necesariamente específicos de la instancia del controlador. Si tenía campos inyectados estáticos en el controlador, y cargó el mismo archivo FXML dos veces y lo mostró dos veces en la interfaz de usuario, entonces no tendría forma de hacer referencia a ambos conjuntos de controles.

Actualización : respuesta a la pregunta en comentarios

En particular, no use campos estáticos solo para permitir que sean accesibles desde fuera de la clase. Un campo estático tiene un valor único que pertenece a la clase, en lugar de un valor para cada instancia de la clase, y la decisión de hacer que los campos sean estáticos solo debe hacerse si tiene sentido. En otras palabras, static define el scope , no la accesibilidad . Para permitir el acceso a los datos de la instancia, solo debe tener una referencia a la instancia. El FXMLLoader tiene un método getController() que le permite recuperar una referencia al controlador.

Un punto relacionado: tampoco es una buena idea exponer los controles de UI del controlador. En su lugar, debe exponer los datos. Por ejemplo, en lugar de definir un método getTextField() en el controlador, en su lugar defina un método textProperty() que devuelva StringProperty represente los contenidos de TextField . La razón de esto es que cuando su jefe llega a la oficina y le dice que quiere que el TextField sea ​​reemplazado por un TextArea , o un ComboBox , o algún otro control, entonces será mucho más difícil si las clases afuera del controlador están usando su TextField . Es mucho menos probable que cambie la estructura de los datos representados por su controlador que la implementación de cómo se presentan esos datos al usuario.

Para algunos ejemplos