Java: setCellValuefactory; Lambda vs. PropertyValueFactory; advantages/disadvantages

PropertyValueFactory expects correctly named property getters. getAColumnsProperty is probably not one.

In case of new PropertyValueFactory<Appointment, LocalDate>("date") the Appointment class needs to contain a dateProperty() method; the returned values need to extend ReadOnlyProperty for this to work and any edits will only lead to an update in the model automatically, if the returned object also WritableValue.

Example Appointment class that should work with PropertyValueFactory<>("date"):

public class Appointment {
    private final ObjectProperty<LocalDate> date = new SimpleObjectProperty<>();

    public final LocalDate getDate() {
        return this.date.get();
    }

    public final void setDate(LocalDate value) {
        this.date.set(value);
    }

    public final ObjectProperty<LocalDate> dateProperty() {
        return this.date;
    }
}

If no such method exists, PropertyValueFactory will use a getter to retrieve the value, i.e. getDate(), but this case updates in the model will not be visible in the UI until it updates the Cell, since the PropertyValueFactory “does not know” where to add a listener.

Disadvantages of PropertyValueFactory

  • Can only find public methods in a public class
  • PropertyValueFactory uses reflection
  • Not typesafe. In new PropertyValueFactory<Appointment, LocalDate>("date") the compiler does not check, if there is a appropriate method, if that method even returns a suitable class or if e.g. the property getter returns a String instead of a ReadOnlyProperty<LocalDate> which can lead to ClassCastExceptions.
  • No compile time checking. In the lambda expression the compiler can do some checking if the method exists and returns a appropriate type; with PropertyValueFactory this is not done.
  • Does not work with records.

If you are sure to implement the appropriate methods in the item class correctly, there is nothing wrong with using PropertyValueFactory, but as mentioned above it has it’s disadvantages. Moreover implementing the Callback is much more flexible. You could e.g. do some additional modifications:

TableColumn<Appointment, String> column = ...

column.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Appointment, String>, ObservableValue<String>> {
    @Override
    public ObservableValue<String> call(TableColumn.CellDataFeatures<Appointment, String> cd) {
        Appointment a  = cd.getValue();

        return Bindings.createStringBinding(() -> "the year: " + a.getDate().getYear(), a.dateProperty());
    }

});

Leave a Comment