In a JavaFX application, javafx.application.Application must be subclassed, and the inherited launch() method, although it's public, must be called from within this derived class, otherwise an exception is thrown. The launch() method then uses reflection to instantiate the derived class, making it difficult to set values for the class members without losing them when launching. All that appears totally unusual to me, and I was wondering why starting a JavaFX application is so complicated, if that kind of software design (design pattern?) has a name, or if it's just bad design?
EDIT:
To be more specific, I want to use the observer pattern, so my java application gets notified when a document was loaded, like this:
public class MyDocumentLoader extends Application
{
private ChangeListener<Worker.State> changeListener;
public void setChangeListener(ChangeListener<Worker.State> changeListener)
{
this.changeListener = changeListener;
}
...
public void loadDocument(String url)
{
webEngine.getLoadWorker().stateProperty().addListener(changeListener);
webEngine.load(url);
}
...
}
I need the callback member in several methods, and ideally I can have more than one instances of the class that loads documents, so I can set different ChangeListeners for different URLs.
My guess is that this design was motivated by the (vast) number of Swing applications that were incorrectly written, with the "primary"
JFrame
s being instantiated and shown on the wrong thread (i.e. not on the AWT event dispatch thread). My guess is that so many Swing applications were incorrectly written that they had to defensively code the framework against the incorrect usage, and that they wanted to avoid this scenario with JavaFX.Forcing (well, almost forcing, there are hack-arounds) an FX Application to start this way makes it much harder to write an application incorrectly in a similar way. The
launch
method (and the equivalent Oracle JVM startup process if you have anApplication
subclass without amain
method and a call tolaunch
) does quite a bit of boilerplate work: it starts the FX toolkit, instantiates theApplication
subclass and calls itsinit()
method, then on the FX Application Thread it instantiates the primaryStage
and passes it to theApplication
subclass'sstart(...)
method. This then ensures everything is running on the correct thread.You should basically consider the
start(...)
method in a JavaFX application as the replacement for themain(...)
method in a "traditional" Java application, with the understanding it is invoked on the FX Application Thread.My recommendation is that the
Application
subclass should be as minimal as possible; it should just delegate to something else to actually create the UI, and then should just place it in the primary stage and show it. Include amain
method that does nothing other than calllaunch(...)
as a fallback for non-JavaFX-aware JVMs. You should only have one instance of oneApplication
subclass present in any JVM. This way yourApplication
subclass has no class members to set, and so the issues you describe simply don't arise.If you use FXML, this is actually fairly natural: the
start(...)
method essentially just delegates to the FXML-controller pair to do the real work. If you don't use FXML, create a separate class to do the actual layout, etc, and delegate to it. See this related question which gets at the same kind of idea.Note also that your statement
is not entirely accurate, as there is an overloaded form of the
launch(...)
method in which you can specify the application subclass. So, if you really need, you can just create a stub for starting the FX toolkit:Now you can do:
Note that
launch
does not return until the FX toolkit shuts down, so it is imperative to put this call in another thread. This potentially creates race conditions, where you may try to do something needing the FX toolkit beforelaunch(...)
has actually initialized it, so you should probably guard against that:and then
SSCCE (I just used inner classes for everything so this is convenient to run, but in real life these would be standalone classes):
JavaFX supports a great number of deployment and packaging strategies, ref. https://docs.oracle.com/javase/8/docs/technotes/guides/deploy/toc.html, and having a standardized lifecycle entry- and exit-point simplifies supporting all these strategies.
If you are struggling to initialize your main application class, due to it being instanciated by the JavaFX launcher, your best option is to use the Application.init() and Application.stop() methods, as James_D points out.