Issue
I have two packages, one for objects and the other for creating GUI. I am trying to create a TableView
in JavaFX and I have to use PropertyValuesFactory<>(property)
to get the value of the object. So I have a bug where if I put the Person and GUI class in the same package everything would work for fine, but when the Person class is put in the objects package everything goes bad and I get this error:
WARNING: Can not retrieve property 'name' in PropertyValueFactory: javafx.scene.control.cell.PropertyValueFactory@6771b7fb with provided class type: class uni.rest.objects.Person
java.lang.RuntimeException: java.lang.IllegalAccessException: module javafx.base cannot access class uni.rest.objects.Person (in module main) because module main does not open uni.rest.objects to javafx.base
This is also the line of code where the error happens:
person_column.setCellValueFactory(new PropertyValueFactory<>("name"));
So I assume that the property value location might have to be changed... I would appreciate any help.
Solution
The PropertyValueFactory
class uses reflection to access the model class' properties. The module system added in Java 9 adds greater encapsulation which prevents modules from reflectively accessing other modules unless given permission by directives in module-info.java
.
The documentation of PropertyValueFactory
mentions what you need to do if deploying your application as a module:
Deploying an Application as a Module
If the referenced class is in a named module, then it must be reflectively accessible to the
javafx.base
module. A class is reflectively accessible if the module opens the containing package to at least thejavafx.base
module. Otherwise thecall(TableColumn.CellDataFeatures)
method will log a warning and returnnull
.For example, if the
Person
class is in thecom.foo
package in thefoo.app
module, themodule-info.java
might look like this:module foo.app { opens com.foo to javafx.base; }
Alternatively, a class is reflectively accessible if the module exports the containing package unconditionally
Another option is to forgo PropertyValueFactory
and use a custom Callback
. The PropertyValueFactory
was more of a convenience for when lambdas were not a thing; before lambdas, if one wanted to use a custom Callback
, one would have to create an anonymous class each time which was verbose. Since lambdas, however, one can do:
person_column.setCellValueFactory(features -> features.getValue().nameProperty());
Obviously, this works best if the model exposes a JavaFX property. The advantages of a custom Callback
include avoidance of reflection and type safety.
Answered By - Slaw