Attaching an InvalidationListener is not documented to cause an eager evaluation like ChangeListener does: ObservableValue's docs states:
"Important note: attaching a ChangeListener enforces eager computation even if the implementation of the ObservableValue supports lazy evaluation."
However, the internal class com.sun.javafx.binding.ExpressionHelper, which manages adding and removing listener operations, explicitly computes the value upon attaching an InvalidationListener:
public static <T> ExpressionHelper<T> addListener(ExpressionHelper<T> helper, ObservableValue<T> observable, InvalidationListener listener) {
if ((observable == null) || (listener == null)) {
throw new NullPointerException();
}
observable.getValue(); // validate observable
return (helper == null)? new SingleInvalidation<T>(observable, listener) : helper.addListener(listener);
}
The line `observable.getValue(); // validate observable` is the explicit evaluation. The class SingleInvalidation does not do any evaluations.
For comparison, ChangeListener uses:
public static <T> ExpressionHelper<T> addListener(ExpressionHelper<T> helper, ObservableValue<T> observable, ChangeListener<? super T> listener) {
if ((observable == null) || (listener == null)) {
throw new NullPointerException();
}
return (helper == null)? new SingleChange<T>(observable, listener) : helper.addListener(listener);
}
But in the constructor of SingleChange the evaluation happens in the line `this.currentValue = observable.getValue();` so it can be stored to observe changes. This is fine.
This causes odd behavior such as the one in JDK-8089579.
"Important note: attaching a ChangeListener enforces eager computation even if the implementation of the ObservableValue supports lazy evaluation."
However, the internal class com.sun.javafx.binding.ExpressionHelper, which manages adding and removing listener operations, explicitly computes the value upon attaching an InvalidationListener:
public static <T> ExpressionHelper<T> addListener(ExpressionHelper<T> helper, ObservableValue<T> observable, InvalidationListener listener) {
if ((observable == null) || (listener == null)) {
throw new NullPointerException();
}
observable.getValue(); // validate observable
return (helper == null)? new SingleInvalidation<T>(observable, listener) : helper.addListener(listener);
}
The line `observable.getValue(); // validate observable` is the explicit evaluation. The class SingleInvalidation does not do any evaluations.
For comparison, ChangeListener uses:
public static <T> ExpressionHelper<T> addListener(ExpressionHelper<T> helper, ObservableValue<T> observable, ChangeListener<? super T> listener) {
if ((observable == null) || (listener == null)) {
throw new NullPointerException();
}
return (helper == null)? new SingleChange<T>(observable, listener) : helper.addListener(listener);
}
But in the constructor of SingleChange the evaluation happens in the line `this.currentValue = observable.getValue();` so it can be stored to observe changes. This is fine.
This causes odd behavior such as the one in JDK-8089579.
- blocks
-
JDK-8089579 'then()' is executed while 'when()' produces 'false'
- Open