Supposedly lazy binding gets evaluated right away
John Hendrikx
hjohn at xs4all.nl
Tue Aug 1 20:56:05 UTC 2017
My understanding of Bindings has always been that they're supposedly
only evaluated when actually used (ie. get() is called on them).
Javadoc for Binding claims (emphasis mine):
"All bindings in the JavaFX runtime are calculated **lazily**. That
means, if a dependency changes, the result of a binding is not
immediately recalculated, but it is marked as invalid. Next time the
value of an invalid binding is requested, it is recalculated."
So, when I create a binding, I donot expect it to be evaluated. This
holds true in certain cases. However, when I then create a binding that
refers another binding, it *DOES* get evaluated at creation time.
Please help me understand why this is happening...
The attached code shows an example of this happening (a WARNING is
logged, and with a different log configuration you can see an exception
logged as well, see at end):
Exception while evaluating select-binding [muted]
Property 'muted' in ObjectProperty [value: null] is null
The problem seems to be this line of code in
com.sun.javafx.binding.ExpressionHelper#addListener which calls getValue
but discards the result (a red flag IMHO):
observable.getValue(); // validate observable
This is breaking the lazy binding and triggers a chain of "get" calls
without the binding being used -- and for what? To discard the result...
The reason why my property is still "null" is because in a complicated
framework you may still be in the process of setting up bindings. In my
case the "playerProperty" is actually set up after the creation of
another class that creates bindings based on this. However that class
gets this WARNING log + stacktrace while only doing binding setups.
Note the property is never null when it is actually used (ie, displayed
somewhere). If the binding was really lazy, then no null should ever be
encountered.
--John
The code:
public static void main(String[] args) {
ObjectProperty<Player> playerProperty = new SimpleObjectProperty<>();
BooleanBinding mutedProperty =
Bindings.selectBoolean(playerProperty, "muted");
new StringBinding() {
{
bind(mutedProperty);
}
@Override
protected String computeValue() {
return "irrelevant";
}
};
// optional, bind playerProperty to something that isn't null
}
public static class Player {
private final BooleanProperty mutedProperty = new
SimpleBooleanProperty();
public BooleanProperty mutedProperty() {
return mutedProperty;
}
}
}
The stacktrace:
java.lang.NullPointerException
at
com.sun.javafx.binding.SelectBinding$SelectBindingHelper.getObservableValue(SelectBinding.java:481)
at
com.sun.javafx.binding.SelectBinding$AsBoolean.computeValue(SelectBinding.java:139)
at javafx.beans.binding.BooleanBinding.get(BooleanBinding.java:157)
at
javafx.beans.binding.BooleanExpression.getValue(BooleanExpression.java:56)
at javafx.beans.binding.BooleanBinding.getValue(BooleanBinding.java:60)
at
com.sun.javafx.binding.ExpressionHelper.addListener(ExpressionHelper.java:54)
at javafx.beans.binding.BooleanBinding.addListener(BooleanBinding.java:76)
at javafx.beans.binding.StringBinding.bind(StringBinding.java:102)
at
hs.mediasystem.ext.media.newstyle.BindingBug$1.<init>(BindingBug.java:37)
at hs.mediasystem.ext.media.newstyle.BindingBug.main(BindingBug.java:35)
More information about the openjfx-dev
mailing list