Problemi di compatibilità con javafx 8 – Campi statici FXML

Ho progettato un’applicazione javafx che funziona bene con jdk 7. Quando provo a eseguirlo in java 8, ottengo le seguenti eccezioni:

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) 

Ho scoperto che il motivo per questo è nel metodo di inizializzazione della class controller, non sono in grado di utilizzare i metodi incorporati in alcun componente statico. (Ad esempio: staticMyTextField.setText () causa il problema in java 8 ma non in java 7). Non sono in grado di trovare nulla di ciò che è stato documentato nelle guide di javafx. Qualcuno può fornire qualche idea sul perché questo sta causando un problema in Java 8? E anche condividere documenti relativi a questo se ce ne sono.

Sembra che tu stia cercando di iniettare un campo di TextField in un campo statico. Qualcosa di simile a

 @FXML private static TextField myTextField ; 

Questo apparentemente ha funzionato in JavaFX 2.2. Non funziona in JavaFX 8. Dal momento che nessuna documentazione ufficiale ha mai supportato questo uso, in realtà non viola la retrocompatibilità, anche se in tutta onestà la documentazione su esattamente ciò che fa FXMLLoader è piuttosto dolorosa.

Non ha molto senso rendere statici i campi @FXML @FXML. Quando carichi un file FXML, crea nuovi oggetti per ciascuno degli elementi nel file FXML. Una nuova istanza controller è associata a ogni chiamata a FXMLLoader.load(...) ei campi in FXMLLoader.load(...) controller vengono iniettati con gli oggetti corrispondenti creati per gli elementi FXML. Quindi i campi iniettati sono necessariamente specifici dell’istanza del controllore. Se nel controller erano presenti campi iniettati statici e si caricava lo stesso file FXML due volte e lo si visualizzava due volte nell’interfaccia utente, non si avrebbe alcun modo di fare riferimento a entrambi i gruppi di controlli.

Aggiornamento : risposta alla domanda nei commenti

In particolare, non utilizzare campi statici solo per consentire loro di essere accessibili dall’esterno della class. Un campo statico ha un singolo valore appartenente alla class, invece di un valore per ogni istanza della class, e la decisione di rendere statici i campi dovrebbe essere fatta solo se ciò ha senso. In altre parole, la static definisce l’ ambito , non l’ accessibilità . Per consentire l’accesso ai dati di istanza, è sufficiente avere un riferimento all’istanza. FXMLLoader ha un metodo getController() che consente di recuperare un riferimento al controller.

Un punto correlato: non è neanche una buona idea esporre i controlli dell’interfaccia utente dal controller. Dovresti invece esporre i dati. Ad esempio, invece di definire un metodo getTextField() nel controller, definire un metodo textProperty() che restituisce una StringProperty rappresenta il contenuto di TextField . La ragione di questo è che quando il tuo capo arriva in ufficio e ti dice che vuole che il TextField sia sostituito da un TextArea , o da un ComboBox , o da qualche altro controllo, allora sarà molto più difficile se le classi all’esterno del controller stanno usando il tuo TextField . La struttura dei dati rappresentata dal controller è molto meno probabile che cambi rispetto all’implementazione del modo in cui i dati vengono presentati all’utente.

Per alcuni esempi