JEP Proposal: Fluent Bindings for multiple observables
Nir Lisker
nlisker at gmail.com
Sun Oct 26 12:56:40 UTC 2025
When I need to combine observable values I usually do something like:
Bindings.createDoubleBinding(() -> width.get() / height.get(), height,
width);
which is much less cumbersome than subclassing (although a bit less
performant I think). It works for an arbitrary number of observables
(including) observable lists/sets/maps too:
Bindings.createDoubleBinding(() -> list.getFirst() / height.get() *
map.get("A"), height, list, map); // assume this makes sense somehow
ReactFX, which is the go-to library for such extension, has a 'combine'
method that work like this:
Val.combine(height, width, (h, w) -> w / h);
with overloads for up to 6 observables (with a hexafunction). For an
arbitrary number, you need the 'create' method that works very much like
the JavaFX Bindings one:
Val.create(() -> a.get() * b.get() * c.get(), a, b, c);
To assess the proposal, I tried to write your JEP examples with the current
JavaFX.
Multi-stage chain (not sure how you mapped to a Point3D from a Point2D and
a number):
ObjectBinding<Point2D> point2d = Bindings.createObjectBinding(() -> new
Point2D(x.get(), y.get()), x, y);
ObjectBinding<Point3D> point3d = Bindings.createObjectBinding(() -> new
Point3D(point2d.get().getX(), point2d.get().getY(), z.get()), point2d, z);
Combining chains:
ObjectBinding<Point2D> point1 = Bindings.createObjectBinding(() -> new
Point2D(x.get(), y.get()), x, y);
ObjectBinding<Point2D> point2 = Bindings.createObjectBinding(() -> new
Point2D(x.get(), y.get()), x, y);
ObjectBinding<Line2D> line = Bindings.createObjectBinding(() -> new
Line2D(point1, point2), point1, point2); // Line2D is not a JavaFX class
Using a default value:
ObservableValue<Point2D> point1 = Bindings.createObjectBinding(() -> new
Point2D(x.get(), y.get()), x, y).orElse(Point2D.ZERO);
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.
Also, static imports can make these calls less ceremonious:
createObjectBinding(() -> new Point2D(x.get(), y.get()), x, y);
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/20251026/7a7488f1/attachment-0001.htm>
More information about the openjfx-dev
mailing list