3

I am working on a Java 8 desktop application using JavaFX 8.
I have this method in the MainApp class (the one that extends the Application class).

public void showUserLayout() {
    try {
        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(MainApp.class.getResource("view/userLayout.fxml"));
        AnchorPane userPane = (AnchorPane) loader.load();

        rootAnchorPane.getChildren().clear();
        rootAnchorPane.getChildren().add(userPane);

        userLayoutController controller = loader.getController();
        controller.setMainApp(this);
    } catch (IOException e) {
        // Handle Exception
    }
}

and I am using the same code for each layout I want to load.

Is there any way to create a method that accepts the class type as a parameter and does the exact same job, for example:

public void genericLayoutLoader(String fxmlFilename, Class rootFXMLElement, Class fxmlController) {
    try {
        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(MainApp.class.getResource(fxmlFilename));
        // Not sure for the Object below
        Object chooseUserAndInterval = (rootFXMLElement) loader.load();
        // rootAnchorPane is same for every layout
        rootAnchorPane.getChildren().clear();
        rootAnchorPane.getChildren().add((rootFXMLElement) chooseUserAndInterval);

        Object controller = (fxmlController) loader.getController();
        ((fxmlController)controller).setMainApp(this);
    } catch (IOException e) {
        // Handle Exception
    }
}

I would use it like this:

public void showUserLayout() {
    genericLayoutLoader("view/userLayout.fxml", AnchorPane, userLayoutController);  
}

Is there any way to achieve this behavior?

Chris
  • 3,619
  • 8
  • 44
  • 64

3 Answers3

1

If you want to stick to using Classes as parameters, your code should look somewhat like this:

    public void genericLayoutLoader(String fxmlFilename, Class rootFXMLElement, Class fxmlController) {
        try {
            FXMLLoader loader = new FXMLLoader();
            loader.setLocation(MainApp.class.getResource(fxmlFilename));
            // Not sure for the Object below
            Object chooseUserAndInterval = loader.load();
            // rootAnchorPane is same for every layout
            rootAnchorPane.getChildren().clear();
            rootAnchorPane.getChildren().add(chooseUserAndInterval);

            Object controller = loader.getController();
            fxmlController.getMethod("setMainApp", new Class[] { MainApp.class }).invoke(controller, this);
        } catch (IOException e) {
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }
    }

    public void showUserLayout() {
        genericLayoutLoader("view/userLayout.fxml", AnchorPane.class, Controller.class);  
    }

But, I still suggest to try to solve this using interfaces if possible.

Dakshinamurthy Karra
  • 5,353
  • 1
  • 17
  • 28
0

You can use an interface as a parameter in the method. Then pass in implementations of the interface as a parameter when you call the method.

interface Loader {
    void load();
}

interface Controller {
    void setMainApp(Object mainApp);
}

Here is a class that implements the loader interface:

class AnchorPane implements Loader {
    void load() {
        System.out.println("Loading...");
    }
}

And here is a class that implements the Controller interface:

class UserLayoutController implements Controller {
    void setMainApp(Object mainApp) {
        System.out.println("Setting main app...");
    }
}

Change the method definition to this:

public void genericLayoutLoader(String fxmlFilename, Loader loader, Controller controller) {
    try {
        FXMLLoader fxmlLoader = new FXMLLoader();
        fxmlLoader.setLocation(MainApp.class.getResource(fxmlFilename));

        // rootAnchorPane is same for every layout
        rootAnchorPane.getChildren().clear();
        rootAnchorPane.getChildren().add(loader);

        controller.setMainApp(this);
    } catch (IOException e) {
        // Handle Exception
    }
}

Then call it somewhere in your code:

genericLayoutLoader("view/userLayout.fxml", new AnchorPane(), new UserLayoutController());
Vito Royeca
  • 657
  • 10
  • 20
  • 1. In JavaFX, the AnchorPane class is provided, and I am not extending it, instead just using it as a member. So the `loader interface` would be implemented in each controller? In that case, that means a new load method, for each controller. I want to avoid creating methods that do the same thing, only with different objects. 2. I think that this line `rootAnchorPane.getChildren().add(loader);` isn't equivalent to passing the actual AnchorPane object, after I've loaded it from the FXML Loader. I might be missing sth with this notation though, as I am not that experienced with Java yet. – Chris Aug 24 '15 at 13:36
  • [2-continue] Also, the loader interface, has a method that returns nothing, so how would that `.add(loader)` work? – Chris Aug 24 '15 at 13:37
0

1) For the Type of object which you are not sure, consider using a wildcard.

Refer to this: When to use generic methods and when to use wild-card?

2) Once you get the object, now you can use the instance Of operator to check the type of object and perform steps specific to this object like:-

if(object instanceof AnchorPane){
//do this
} else if(object instance of  BorderPane){
//do this
}
Community
  • 1
  • 1
Amit Bhati
  • 5,569
  • 1
  • 24
  • 45