JavaFX InvalidationListener or ChangeListener

Viewed 7445

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).

3 Answers

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.

There are two rules to remember, if you plan to use an InvalidationListener.

  1. Make sure it is no problem that your listener can be called, even if no change has happened.
  2. If you want your listener to be fired for every change that happens (and usually this is the purpose of a listener), make sure that the listener calls the getter(s) of the observable(s), where it is registered.

Otherwise switch to ChangeListener.

Related