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)

Reply via email to