Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

java

javafx

today i encountered another thing i don't really understand while trying to learn more about JavaFX and Java in general.

Reference is the following tutorial (im trying to apply the principle to an organizer):

JavaFX 8 Tutorial

I will give a short outline of the particular part on which i've got a question:

My main window contains a tableview which shows some appointment-data. So i got some lines of this style(same as in the tutorial):

aColumn.setCellValueFactory(cellData ->cellData.getValue().getAColumnsProperty());

The data can be manipulated via an additional EditDialog. That works just fine. If i edit things the changes are displayed immediately but i did some additional research to better understand the Lambda (not too successful). Now...in the online java documentation Java Doc PropertyValueFactory it says: "A convenience implementation of the Callback-Interface,[...]"

So i refactored my code into this style:

aColumn.setCellValueFactory(new PropertyValueFactory<Appointment,LocalDate>("date"));

Which i find much more readable than the Lambda. But i noticed that when i make changes i need to do some sorting on the TableView before the changes are displayed.

Is it possible to achieve an immediate display of change in the second approach?

If yes: are there major disavantages which would discourage such a modification? I.e. would the Lambda be the best practice in this situation?

I appreciate any help.

like image 480
Wolfone Avatar asked Jun 27 '16 08:06

Wolfone


1 Answers

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());
    }

});
like image 67
fabian Avatar answered Oct 22 '22 05:10

fabian