[prev in list] [next in list] [prev in thread] [next in thread] 

List:       openjdk-openjfx-dev
Subject:    Re: RFR: 8274771: Map, FlatMap and OrElse fluent bindings for ObservableValue [v8]
From:       John Hendrikx <jhendrikx () openjdk ! java ! net>
Date:       2022-01-27 21:49:07
Message-ID: fiSbTzoI7mAdybckSGuQWyi_Vl03f2M980GL3FTZQlg=.40f32775-e173-45ae-a305-a888f59ee330 () github ! com
[Download RAW message or body]

> 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:

  Fix wrong test values

-------------

Changes:
  - all: https://git.openjdk.java.net/jfx/pull/675/files
  - new: https://git.openjdk.java.net/jfx/pull/675/files/14048a99..30733ccb

Webrevs:
 - full: https://webrevs.openjdk.java.net/?repo=jfx&pr=675&range=07
 - incr: https://webrevs.openjdk.java.net/?repo=jfx&pr=675&range=06-07

  Stats: 3 lines in 1 file changed: 1 ins; 0 del; 2 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


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic