I am only interested whether a property has changed or not, but not in the new value.
Is it advantageous to register an InvalidationListener
instead of a ChangeListener
?
I was assuming that a change to a property does first invalidate the property and notifies all invalidation listeners. Only if there are change listeners registered, or somebody requests this property, the property is 'validated'/ re-computed and all change listeners are updated with the new value.
Since I am not interested in the actual value, I assume it is a performance advantage to listen only for invalidation events (property has changed but was not re-calculated, some kind of intermediate state).
You need to implement a ChangeListener
for that. A InvalidationListener
is only executed once the value becomes invalid. See the docs.
From the java docs of ObservableValue:
An ObservableValue generates two types of events: change events and invalidation events. A change event indicates that the value has changed. An invalidation event is generated, if the current value is not valid anymore. This distinction becomes important, if the ObservableValue supports lazy evaluation, because for a lazily evaluated value one does not know if an invalid value really has changed until it is recomputed. For this reason, generating change events requires eager evaluation while invalidation events can be generated for eager and lazy implementations.
I have added a simple example
public static void main(String[] args) {
SimpleIntegerProperty one = new SimpleIntegerProperty(1);
SimpleIntegerProperty two = new SimpleIntegerProperty(0);
// the binding we are interested in
NumberBinding sum = one.add(two);
sum.addListener(observable -> System.out.println("invalidated"));
// if you add a value change listener, the value will NOT be evaluated lazy anymore
//sum.addListener((observable, oldValue, newValue) -> System.out.println("value changed from " + oldValue + " to " + newValue));
// is valid, since nothing changed so far
System.out.println("sum valid: " + sum.isValid());
// will invalidate the sum binding
two.set(1);
one.set(2); // invalidation event NOT fired here!
System.out.println("sum valid: " + sum.isValid());
// will validate the sum binding, since it is calculated lazy when getting the value
System.out.println("sum: " + sum.getValue());
System.out.println("sum valid: " + sum.isValid());
}
The problem in using the InvalidationListener
is that you will not be notified for changes if the value becomes invalid again, since it is already invalid. You have to use a change listener for that.
Registering a change listener on the property will disable lazy evaluation, so the invalidation event gets fired every time the change listener gets fired.
Try it out in the sample i added.
from the book :
An observable should generate an invalidation event only when the status of its content changes from valid to invalid. That is, multiple invalidation in a row should generate only one invalidation event.
a tiny example to show that
public class stackOverflowListenerQuestion extends Application {
public static void main( String[] args ) {
launch();
}
@Override
public void start( Stage primaryStage ) throws Exception {
IntegerProperty money = new SimpleIntegerProperty(1);
money.addListener(observable -> System.out.println("we should notify the listener"));
money.set(10);
money.set(20);
money.set(30);
System.out.println(money.getValue());
IntegerProperty moreMoney = new SimpleIntegerProperty(1);
moreMoney.addListener(( observable, oldValue, newValue ) -> System.out.println("we should notify the listener very quickly"));
moreMoney.set(100);
moreMoney.set(200);
moreMoney.set(300);
System.out.println(moreMoney.getValue());
Platform.exit();
}
}
output
we should notify the listener
30
we should notify the listener very quickly
we should notify the listener very quickly
we should notify the listener very quickly
300
The listener associated with the money
property is of type Invalidationlistener
, from the output we can see the differences in term of events between the InvalidationListener
and the ChangeListener
.
One more detailed example:
public class InvalidationListener extends Application {
public static void main( String[] args ) {
launch();
}
@Override
public void start( Stage primaryStage ) throws Exception {
Person p1 = new Person();
Person p2 = new Person();
Person p3 = new Person();
NumberBinding total = Bindings.add(p1.moneyProperty().add(p2.moneyProperty()), p3.moneyProperty());
//to see the differences between InvalidationListener and ChangeListener, yous should test them separately and watch the printed result to understand.
// total.addListener(( observable, oldValue, newValue ) -> System.out.println("change event occurred, we should notify the listeners"));
total.addListener(observable -> System.out.println("Invalidation occurred, we should notify the listeners but lazily"));
p1.setMoney(100);
System.out.println("total.isValid() = " + total.isValid());
p2.setMoney(200);
System.out.println("total.isValid() = " + total.isValid());
p3.setMoney(200);
System.out.println("total.isValid() = " + total.isValid());
System.out.println("total = " + total.getValue());
System.out.println("total.isValid() = " + total.isValid());
p3.setMoney(150);
System.out.println("total.isValid() = " + total.isValid());
System.out.println("total = " + total.getValue());
System.out.println("total.isValid() = " + total.isValid());
Platform.exit();//shutdown the JavaFx Application Thread
}
static class Person{
private IntegerProperty money = new SimpleIntegerProperty();
public final int getMoney() {
return money.get();
}
public final void setMoney( int money ) {
this.money.set(money);
}
public IntegerProperty moneyProperty() {
return money;
}
}
}
When you use ChangeListener
, an event is fired whenever a change has occured. When you use InvalidationListener
it is not the case.
from the same book
A property generates an invalidation event when the status of its value changes from valid to invalid for the first time. Properties in JavaFx use lazy evaluation. When an invalid property becomes invalid again, an invalidation event is not generated. An invalid property becomes valid when it is recomputed, for example, by calling its get() or getValue() method.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With