Home > Software design >  How to pass StringBinding and ReadOnlyProperty as generic arguments?
How to pass StringBinding and ReadOnlyProperty as generic arguments?

Time:01-25

I have two methods in my JavaFX-code for creating TextFields with Bindings. I have to bind ReadOnlyProperty or StringBinding. So I created two methods with different method signatures but with the same code in the block.

How can I simplify my code (with generics?) to handle all the different Properties (String, Long, Object etc.)?

createTextField(knkFile.idProperty().asString(), 1, 0); // ReadOnlyIntegerProperty
createTextField(knkFile.dateProperty().asString(), 1, 1); // ReadOnlyObjectProperty
createTextField(knkFile.fileNameProperty(), 1, 2); // ReadOnlyStringProperty
createTextField(knkFile.lastModifiedProperty().asString(), 1, 5); // ReadOnlyLongProperty
private void createTextField(ReadOnlyProperty property, int column, int row) {
    TextField textField = new TextField();
    textField.textProperty().bind(property);
    this.add(textField, column, row); // add to GridPane
}

private void createTextField(StringBinding binding, int column, int row) {
    TextField textField = new TextField();
    textField.textProperty().bind(binding);
    this.add(textField, column, row);
}

Edit: own answer (deprecated - see accepted answer)

I just saw that StringBinding and ReadOnlyProperty implement ObservableValue. So I only need this method now:

private void createTextField(ObservableValue property, int column, int row) {
    TextField textField = new TextField();
    textField.textProperty().bind(property);
    this.add(textField, column, row);
}

CodePudding user response:

Solution: Use StringExpression

All of the values passed to your createTextField methods are StringExpressions, so you can create a single method that takes a screenshot

import javafx.application.Application;
import javafx.beans.binding.StringExpression;
import javafx.beans.property.*;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;

import java.time.Instant;

public class BindSample extends Application {
    private final FileModel fileModel = new FileModel(
            1, Instant.now(), "test.txt", Instant.now().toEpochMilli()
    );

    private final GridPane form = new GridPane();

    @Override
    public void start(Stage stage) {
        form.setHgap(5);
        form.setVgap(5);
        form.setPadding(new Insets(10));

        form.addColumn(0,
                new Label("Id"),
                new Label("Date"),
                new Label("FileName"),
                new Label(),
                new Label(),
                new Label("Last Modified")
        );

        createTextField(fileModel.idProperty().asString(), 1, 0); // ReadOnlyIntegerProperty
        createTextField(fileModel.dateProperty().asString(), 1, 1); // ReadOnlyObjectProperty
        createTextField(fileModel.fileNameProperty(), 1, 2); // ReadOnlyStringProperty
        createTextField(fileModel.lastModifiedProperty().asString(), 1, 5); // ReadOnlyLongProperty

        stage.setScene(new Scene(form));
        stage.show();
    }

    private void createTextField(
            StringExpression property,
            int column,
            int row
    ) {
        TextField textField = new TextField();
        textField.textProperty().bind(property);
        form.add(textField, column, row);
    }

    public static void main(String[] args) {
        launch();
    }

    private static class FileModel {
        private final ReadOnlyIntegerWrapper id;
        private final ReadOnlyObjectWrapper<Instant> date;
        private final ReadOnlyStringWrapper fileName;
        private final ReadOnlyLongWrapper lastModified;

        public FileModel(int id, Instant date, String fileName, long lastModified) {
            this.id = new ReadOnlyIntegerWrapper(id);
            this.date = new ReadOnlyObjectWrapper<>(date);
            this.fileName = new ReadOnlyStringWrapper(fileName);
            this.lastModified = new ReadOnlyLongWrapper(lastModified);
        }

        public int getId() {
            return id.get();
        }

        public ReadOnlyIntegerProperty idProperty() {
            return id.getReadOnlyProperty();
        }

        public Instant getDate() {
            return date.get();
        }

        public ReadOnlyObjectProperty<Instant> dateProperty() {
            return date.getReadOnlyProperty();
        }

        public String getFileName() {
            return fileName.get();
        }

        public ReadOnlyStringProperty fileNameProperty() {
            return fileName.getReadOnlyProperty();
        }

        public long getLastModified() {
            return lastModified.get();
        }

        public ReadOnlyLongProperty lastModifiedProperty() {
            return lastModified.getReadOnlyProperty();
        }
    }
}

Minor implementation notes

  1. The example uses ReadOnlyPropertys when demonstrating the binding to match your question sample code. But for this simple example, if the model object were immutable, then a Record without Bindings could be used instead. This can simplify the logic if you know the data never changes.

  2. In the example, I use an Instant to represent a date as a precise moment in time, but you could use a ZonedDateTime for better date representation (or LocalDateTime or LocalDate if the ambiguity was OK), see Java date representations if you want to understand this.

  •  Tags:  
  • Related