JavaFX java.lang.IllegalArgumentException: Can not set javafx.scene.control.Label field sample.Controller.location to java.net.URL
Asked Answered
F

1

8

My JavaFX application has a Label with an fx:id of location. It is defined in an FXML file. When I try to run the application, I get the following error:

java.lang.IllegalArgumentException: Can not set javafx.scene.control.Label field sample.Controller.location to java.net.URL

I am using JDK 12 with JavaFX 11.0.2.

I've seen other answers here on SO that say this is caused by a conflicting type for the location Label. For example, it might be declared as a Label in the FXML file but in the Java code it is something else (in this case, java.net.URL). However, as you can see in the code below, I am not using the URL class anywhere.

Changing the fx:id to something else (such as loc) makes the error go away, so location must be a "magic" name.

What is causing this?

sample.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.Pane?>

<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
   <children>
      <Label fx:id="location" layoutX="133.0" layoutY="146.0" text="Output" />
   </children>
</Pane>

Main.java

package sample;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application
{

    @Override
    public void start(Stage primaryStage) throws Exception
    {
        Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
        primaryStage.setTitle("Hello World");
        primaryStage.setScene(new Scene(root, 400, 275));
        primaryStage.show();
    }


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

Controller.java

package sample;

import javafx.fxml.FXML;
import javafx.scene.control.Label;

public class Controller
{
    @FXML
    Label location;

}

module-info.java

module MyTest
{
    requires javafx.controls;
    requires javafx.fxml;
    opens sample;
}
Futile answered 21/5, 2019 at 22:22 Comment(0)
S
9

It is relatively easy to find out by inspecting FXMLLoader:

/**
 * A key for location URL in namespace map.
 * @see #getNamespace()
 * @since JavaFX 2.2
 */
public static final String LOCATION_KEY = "location";
/**
 * A key for ResourceBundle in namespace map.
 * @see #getNamespace()
 * @since JavaFX 2.2
 */
public static final String RESOURCES_KEY = "resources";

private URL location;
private ResourceBundle resources;

private <T> T loadImpl(InputStream inputStream,
                       Class<?> callerClass) throws IOException {
    ...
    namespace.put(LOCATION_KEY, location);
    namespace.put(RESOURCES_KEY, resources);
    ...
}

Basically this means that location (like resources, controller and Controller) is a reserved keyword.

These keywords are added to a namespace (an observable map: ObservableMap<String, Object> namespace), and location is the URL of the FXML file, something like:

namespace.put("location", "file:/path/to/your/FXML/file");

Later on, the different fx:id tags of your FXML file are evaluated and added to the same namespace:

private void processValue() throws LoadException {
    ...
    if (fx_id != null) {
        namespace.put(fx_id, value);
        injectFields(fx_id, value);
    }
    ...
}

In this case, if your fx:id is "location", you will be doing something like:

namespace.put("location", Label@12dafafa);

Obviously, since you are using the same key, you are overwriting the former URL with a new object, in this case a Label.

Later on, when FXMLLoader tries to inject this location:

injectFields(LOCATION_KEY, location);

the exception happens, as the Label that the map gets from the key "location" can be set to the URL field:

Can not set javafx.scene.control.Label field org.openjfx.FXMLController.location to java.net.URL

While it is not documented, this means that you can't use "location" (nor "resources", "controller") as a value for the fx:id tag, because it will collide with these keywords.

Sloth answered 21/5, 2019 at 23:38 Comment(1)
Well, it'd be nice if the Scene Builder program would tell you that. The error message isn't helpful at all. Thanks!Ungley

© 2022 - 2024 — McMap. All rights reserved.