Casey B.
Casey B.

Reputation: 279

JavaFX: ClassNotFoundException for imported class in FXML

I'm trying to create a textfield with a title embedded in the field border like:

enter image description here

Following the solution posted here I've created a .java file called TitledBorder.java within my src>main>java directory. My FXML is in the src>main>resources directory and I've added:

<?import TitledBorder?> at the top and it shows no error like: enter image description here

I then added this code to the FXML

<HBox prefHeight="100.0" prefWidth="200.0">
    <children>
      <TitledBorder title="Email" >
        <TextField fx:id="emailField" prefHeight="44.0" prefWidth="143.0" />
      </TitledBorder>
    </children>
</HBox>

and it shows no error either. I then launch my main method which is in a class also in src>main>java but it gets an error in the .fxml saying javafx.fxml.LoadException: /C:/Users/ME/Documents/Automation/target/classes/demofxml.fxml

and

Caused by: java.lang.ClassNotFoundException
    at javafx.fxml.FXMLLoader.loadType(FXMLLoader.java:2899)

I'm not sure why it references "/target/classes/..." as opposed to "/src/main/java/...".

This is the only FXML example I've found so I'm confused why I'm getting an error upon compiling, yet no errors are shown prior? Removing all reference to TitledBorder allows all my code to function/compile properly. Since its in the src package I use this code in FXML to connect w/ controller fx:controller="loadController">. CSS is added properly too.

Thoughts?

Upvotes: 4

Views: 1281

Answers (1)

Uluk Biy
Uluk Biy

Reputation: 49185

The line

<?import TitledBorder?>

implies that you put the TitledBorder.java file to default package (i.e. no package declaration in source code of this file). However FXMLLoader's source code checks the imports in FXML file and splits package path name and class name in loadType(...) below, to load the imported class later with loadTypeForPackage():

private Class<?> loadType(String name, boolean cache) throws ClassNotFoundException {
    int i = name.indexOf('.');
    int n = name.length();
    while (i != -1
        && i < n
        && Character.isLowerCase(name.charAt(i + 1))) {
        i = name.indexOf('.', i + 1);
    }

    if (i == -1 || i == n) {
        throw new ClassNotFoundException();
    }

    String packageName = name.substring(0, i);
    String className = name.substring(i + 1);

    Class<?> type = loadTypeForPackage(packageName, className);

    if (cache) {
        classes.put(className, type);
    }

    return type;
}

// TODO Rename to loadType() when deprecated static version is removed
private Class<?> loadTypeForPackage(String packageName, String className) throws ClassNotFoundException {
    return getClassLoader().loadClass(packageName + "." + className.replace('.', '$'));
}

The imported class name is "TitledBorder" so the variable i at the 1st line in loadType method will be evaluated as name.indexOf('.') = -1, and will throw ClassNotFoundException in the next lines of code.

Generally it is bad practice to use default packages. Put the TitledBorder.java into some package and import it as

<?import my.some.package.TitledBorder?>

Upvotes: 1

Related Questions