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