JEP Proposal: Fluent Bindings for multiple observables

John Hendrikx john.hendrikx at gmail.com
Sun Nov 30 14:46:20 UTC 2025


On 26/10/2025 13:56, Nir Lisker wrote:
>
> Some observations:
> Bindings returns a Binding rather than an ObservableValue and also has
> primitive specialization versions that have their unique methods (add,
> subtract...). These methods, however, can be replicated with fluent
> bindings and they also have the "known" subtle GC issue for the
> intermediary values.

The subtle GC is rather easy to hit I saw.

I often write code that has very little assignments, and operates by
using listeners/subscriptions, and this code is already incorrect:

// All local variables: ObjectProperty<LocalDate> departureDate =
newSimpleObjectProperty<>();

ObjectProperty<LocalDate> returnDate = newSimpleObjectProperty<>();

ObservableValue<Boolean> returnTrip = tripType.map(v -> v.equals("Return"));

BooleanBinding datesValid = Bindings.createBooleanBinding(() -> {

returnreturnTrip.getValue() && departureDate.get() != null&&
returnDate.get() != null?
departureDate.get().compareTo(returnDate.get()) < 0 : false;

}, departureDate, returnDate, returnTrip);

... create some UI where dp is a DatePicker ...

datesValid.subscribe(valid -> {

if(valid) {

dp.getStyleClass().remove("invalid");

}

else{

dp.getStyleClass().add("invalid");

}

});

It works perfectly, even for a longer period, until I added a few
`System.gc()` calls, and the binding just poofs despite having a
subscription on it.

It's really disingenious, nothing is mentioned in the docs that the
above is incorrect usage:

- Not in Bingdings#createBooleanBinding
- Not in BooleanBinding
- Not in Binding

The only thing mentioned is that you can use `unbind` to stop observing
(or just wait for a random GC to occur obviously...)

--John


>
> The proposal is more ergonomic with its fluency for a couple of
> values, but I'm not sure it solves enough problems that the current
> mechanism can't.
>
> On Sun, Oct 26, 2025 at 11:59 AM John Hendrikx
> <john.hendrikx at gmail.com> wrote:
>
>     JEP: https://gist.github.com/hjohn/611acb65769b68a845b8919c62a3e99a
>
>     Hi everyone,
>
>     I'd like to propose an extension to the fluent bindings API on
>     ObservableValue (map, flatMap, orElse) which were introduced in JavaFX
>     19 over 3 years ago.
>
>     The API currently is very powerful when dealing with a single
>     observable, but lacks support when dealing with multiple observables. 
>     For example, let's say you want to compute a width/height ratio.  You
>     could write this:
>
>         ObservableValue<Double> ratio = width.map(w -> w / height.get());
>
>     ... but you'll quickly find that such an observable will not update
>     itself when height changes, only when width changes.
>
>     The go-to solution for this is currently:
>
>         DoubleBinding ratio = new DoubleBinding() {
>             { bind(width, height); }
>
>             protected double computeValue() { return width.get() /
>     height.get(); }
>         }
>
>     My proposal would extend ObservableValue with a new `with` method that
>     returns an intermediate stage that can be easily converted back to an
>     ObservableValue:
>
>         ObservableValue<Double> ratio = width.with(height).map((w, h)
>     -> w /
>     h);  // yields a ratio that updates whenever w or h changes
>
>     Or for example:
>
>          ObservableValue<Point> point = x.with(y).map(Point::new);  //
>     yields a Point that updates whenever x or y changes
>
>     The intermediate stage would not be an observable value itself.  This
>     limits the API surface, and makes this proposal fairly lightweight and
>     much easier to implement.
>
>     Please see the JEP for the full proposal.  I look forward to your
>     feedback!
>
>     --John
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/openjfx-dev/attachments/20251130/6f9702fa/attachment-0001.htm>


More information about the openjfx-dev mailing list