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?
<?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>
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);
}
}
package sample;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
public class Controller
{
@FXML
Label location;
}
module MyTest
{
requires javafx.controls;
requires javafx.fxml;
opens sample;
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With