1
\$\begingroup\$

In JavaFX, the lifecycle of an Application is quite different than Swing. Your constructor must take no arguments so that the Application class can create an instance of your object. Once the instance has been created, it calls start(Stage), which gives you a Stage for you to put your user interface on. This may be fine for some people, but I want to access instance variables of my Application class (which happens to be called LJGM), and the only way I thought I could do this was to have a single, static instance of my LJGM object, and have a static instance() method which returns the instance. Whenever the constructor is called, instance is set to this. There has to be a better way to do this.

public class LJGM extends Application {

    /** The instance of {@link LJGM}. */
    private static LJGM instance;

    /**
     * Gets the only instance of LJGM.
     * 
     * @return The instance of LJGM.
     */
    public static LJGM instance() {
        return instance;
    }

    // (other variables here)

    /**
     * Instantiates a new LJGM object.
     */
    public LJGM() {
        LJGM.instance = this;
        // (initialize other variables)
    }

    /*
     * (non-Javadoc)
     * 
     * @see javafx.application.Application#start(javafx.stage.Stage)
     */
    @Override
    public void start(Stage primaryStage) throws Exception {
        // (add GUI to the primaryStage here)
    }

    /**
     * Main method.
     * 
     * @param args
     *            The arguments to pass to the application.
     */
    public static void main(String[] args) {
        launch(args);
    }
}

Here is the Application Javadoc in case you need it.

\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

One alternative is to use the Borg Pattern. Allow creating multiple instances, all of which, simply share the same state. Most online references will be in Python (where it is real easy to code), but the idea can be transplanted to Java too.

The advantage over the Singleton pattern is that it hides the fact that there is shared state from clients of the class, especially those that are interested in creating instance.

Clients (in casu the JavaFX framework) can simply use the constructor. Your own code can also easily get the Application by creating a separate instance.

so some sample code :

public class LJGM extends Application {

    private static SharedState sharedState = new SharedState();

    private static class SharedState {

        private Object variable;

        private Object getVariable() {
            return variable;
        }
    }

    public LJGM() {
    }

    public Object getVariable() {
        return sharedState.getVariable();
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        // (add GUI to the primaryStage here)
    }

    public static void main(String[] args) {
        launch(args);
    }
}

All that being said, I don't see any reason why you could not simply use dependency injection to avoid having global state here. So rather than having low level components query the Application object, let the Application pass them the parameters they will need, by passing itself (preferably implementing an interface) or by passing in the variables themselves.

public class LJGM extends Application implements ApplicationContext {

    private Object variable;

    public LJGM() {
    }

    public Object getVariable() {
        return variable;
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        // (add GUI to the primaryStage here)
        new LowLevelComponent(this);
        new OtherLowLevelComponent(variable);
    }

    public static void main(String[] args) {
        launch(args);
    }
}
\$\endgroup\$
3
  • \$\begingroup\$ The second idea could work, except for the fact that one of the objects is used for logging, so it doesn't seem like a good idea to have to pass said object into every other object's constructor. \$\endgroup\$
    – mattbdean
    Commented Jul 26, 2013 at 20:58
  • \$\begingroup\$ Ok, but Logging frameworks usually already come with a mechanism to get hold of a Logger without being passed one. So normally you shouldn't need to go through the Application instance for that to begin with. \$\endgroup\$
    – bowmore
    Commented Jul 27, 2013 at 6:25
  • \$\begingroup\$ In that case, this answer can really help me. Thanks a lot :) \$\endgroup\$
    – mattbdean
    Commented Jul 27, 2013 at 14:29
0
\$\begingroup\$

What you have implemented is the Singleton design pattern.

Normally I would suggest that you make public LJGM() private, and public static LJGM instance() check if instance is null, and if so call the constructor to set it, but it looks like you can't do that since your constructor needs to be able to be called from an inheriting class.

What I would recommend you do however is make your constructor protected, and probably rename your public static LJGM instance() method to something like getInstance just to avoid naming conflicts with the private variable. And it may be prudent to assert that instance is null immediately in your constructor.

\$\endgroup\$
2
  • \$\begingroup\$ Actually, my constructor has to be public. I may have worded it strangely in the question, but LJGM's superclass (Application) is calling its constructor, not a subclass. \$\endgroup\$
    – mattbdean
    Commented Jul 26, 2013 at 21:02
  • \$\begingroup\$ Some code from Application: Constructor<? extends Application> c = appClass.getConstructor(); app = c.newInstance(); \$\endgroup\$
    – mattbdean
    Commented Jul 26, 2013 at 21:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.