ObservableValue::map called twice
John Hendrikx
john.hendrikx at gmail.com
Thu Sep 29 14:33:38 UTC 2022
Hi Nir,
This is sort of normal behavior, and is a consequence of how listeners
are only registered when themselves observed. This interacts with the
concept of being valid/invalid. There is a long comment about it in
LazyObjectBinding, but this is the code that is causing this:
private void updateSubscriptionAfterAdd() {
if (!wasObserved) { // was first observer registered?
subscription = observeSources(); // start observing source
/*
* Although the act of registering a listener already
attempts to make
* this binding valid, allowValidation won't allow it as
the binding is
* not observed yet. This is because isObserved will not
yet return true
* when the process of registering the listener hasn't
completed yet.
*
* As the binding must be valid after it becomes observed
the first time
* 'get' is called again.
*
* See com.sun.javafx.binding.ExpressionHelper (which is used
* by ObjectBinding) where it will do a call to
ObservableValue#getValue
* BEFORE adding the actual listener. This results in
ObjectBinding#get
* to be called in which the #allowValidation call will
block it from
* becoming valid as the condition is "isObserved()"; this
is technically
* correct as the listener wasn't added yet, but means we
must call
* #get again to make this binding valid.
*/
get(); // make binding valid as source wasn't tracked until now
wasObserved = true;
}
}
Basically the first "map" call in your sample is triggered by
`observeSources` in the snippet above (triggered in turn by
`ExpressionHelper` call to getValue just before it adds the listener).
The 2nd one is triggered by the `get()` call.
It may be possible to change this but will likely require a change in
the parent class (ObjectBinding) to explicitely set it to valid (which
breaks the encapsulation it does there to guard this).
Alternatively, it may be possible to change the order of calls in
`ExpressionListener`, first add the listener, then make it valid with
`getValue` instead of the other way around. At the time I didn't want to
risk changing that order as I'm unsure if that could have any
consequences, given that it is such an important part of JavaFX properties.
It may also be possible to somehow override what `allowValidation`
returns during the call to `observeSources`.
--John
On 28/09/2022 20:44, Nir Lisker wrote:
> Hi,
>
> Running this code:
>
> var label = new Label();
> var tf = new TextField();
> label.textProperty().bind(tf.textProperty().map(t -> {
> System.out.println("in");
> return t;
> }));
>
> produces the output::
> in
> in
>
> From what I see, the lambda should be called once, but it's called
> twice. Looks like improper behavior to me.
>
> - Nir
More information about the openjfx-dev
mailing list