> This is an implementation of the proposal in > https://bugs.openjdk.java.net/browse/JDK-8274771 that me and Nir Lisker > (@nlisker) have been working on. It's a complete implementation including > good test coverage. > > This was based on https://github.com/openjdk/jfx/pull/434 but with a smaller > API footprint. Compared to the PoC this is lacking public API for > subscriptions, and does not include `orElseGet` or the `conditionOn` > conditional mapping. > > **Flexible Mappings** > Map the contents of a property any way you like with `map`, or map nested > properties with `flatMap`. > > **Lazy** > The bindings created are lazy, which means they are always _invalid_ when not > themselves observed. This allows for easier garbage collection (once the last > observer is removed, a chain of bindings will stop observing their parents) > and less listener management when dealing with nested properties. > Furthermore, this allows inclusion of such bindings in classes such as `Node` > without listeners being created when the binding itself is not used (this > would allow for the inclusion of a `treeShowingProperty` in `Node` without > creating excessive listeners, see this fix I did in an earlier PR: > https://github.com/openjdk/jfx/pull/185) > > **Null Safe** > The `map` and `flatMap` methods are skipped, similar to `java.util.Optional` > when the value they would be mapping is `null`. This makes mapping nested > properties with `flatMap` trivial as the `null` case does not need to be > taken into account in a chain like this: > `node.sceneProperty().flatMap(Scene::windowProperty).flatMap(Window::showingProperty)`. > Instead a default can be provided with `orElse`. > > Some examples: > > void mapProperty() { > // Standard JavaFX: > label.textProperty().bind(Bindings.createStringBinding(() -> > text.getValueSafe().toUpperCase(), text)); > > // Fluent: much more compact, no need to handle null > label.textProperty().bind(text.map(String::toUpperCase)); > } > > void calculateCharactersLeft() { > // Standard JavaFX: > > label.textProperty().bind(text.length().negate().add(100).asString().concat(" > characters left")); > > // Fluent: slightly more compact and more clear (no negate needed) > label.textProperty().bind(text.orElse("").map(v -> 100 - v.length() + " > characters left")); > } > > void mapNestedValue() { > // Standard JavaFX: > label.textProperty().bind(Bindings.createStringBinding( > () -> employee.get() == null ? "" > : employee.get().getCompany() == null ? "" > : employee.get().getCompany().getName(), > employee > )); > > // Fluent: no need to handle nulls everywhere > label.textProperty().bind( > employee.map(Employee::getCompany) > .map(Company::getName) > .orElse("") > ); > } > > void mapNestedProperty() { > // Standard JavaFX: > label.textProperty().bind( > Bindings.when(Bindings.selectBoolean(label.sceneProperty(), "window", > "showing")) > .then("Visible") > .otherwise("Not Visible") > ); > > // Fluent: type safe > label.textProperty().bind(label.sceneProperty() > .flatMap(Scene::windowProperty) > .flatMap(Window::showingProperty) > .orElse(false) > .map(showing -> showing ? "Visible" : "Not Visible") > ); > } > > Note that this is based on ideas in ReactFX and my own experiments in > https://github.com/hjohn/hs.jfx.eventstream. I've come to the conclusion > that this is much better directly integrated into JavaFX, and I'm hoping this > proof of concept will be able to move such an effort forward.
John Hendrikx has updated the pull request incrementally with one additional commit since the last revision: Apply changes suggested in review and updated copyright years to 2022 ------------- Changes: - all: https://git.openjdk.java.net/jfx/pull/675/files - new: https://git.openjdk.java.net/jfx/pull/675/files/312fb506..30e8ceac Webrevs: - full: https://webrevs.openjdk.java.net/?repo=jfx&pr=675&range=03 - incr: https://webrevs.openjdk.java.net/?repo=jfx&pr=675&range=02-03 Stats: 182 lines in 11 files changed: 127 ins; 13 del; 42 mod Patch: https://git.openjdk.java.net/jfx/pull/675.diff Fetch: git fetch https://git.openjdk.java.net/jfx pull/675/head:pull/675 PR: https://git.openjdk.java.net/jfx/pull/675