我只对属性是否发生变化感兴趣,而不关心具体的新值。
注册一个InvalidationListener
是否比注册一个ChangeListener
更有优势?
我之前认为改变属性会先使该属性失效并通知所有失效监听器。只有在有注册的改变监听器或有人请求此属性时,该属性才会被“验证”/重新计算,并使用新值更新所有改变监听器。
由于我对实际值不感兴趣,因此我假设仅监听失效事件(属性已更改但未被重新计算,一种中间状态)可以提高性能。
我只对属性是否发生变化感兴趣,而不关心具体的新值。
注册一个InvalidationListener
是否比注册一个ChangeListener
更有优势?
我之前认为改变属性会先使该属性失效并通知所有失效监听器。只有在有注册的改变监听器或有人请求此属性时,该属性才会被“验证”/重新计算,并使用新值更新所有改变监听器。
由于我对实际值不感兴趣,因此我假设仅监听失效事件(属性已更改但未被重新计算,一种中间状态)可以提高性能。
ChangeListener
。只有当值变得无效时,才会执行InvalidationListener
。请参见文档。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());
}
InvalidationListener
的问题在于,如果值再次变为无效状态,您将不会收到更改通知,因为它已经是无效的。您需要使用更改监听器来实现这一点。以下是一个小例子以说明这一点。仅当其内容状态从有效更改为无效时,可观察对象才应生成一次失效事件。也就是说,连续多个失效应该只生成一个失效事件。
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();
}
}
输出
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
与 money
属性关联的侦听器是 Invalidationlistener
类型的,从输出中我们可以看到 InvalidationListener
和 ChangeListener
之间在事件方面的差异。
再举一个详细的例子:
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;
}
}
}
ChangeListener
时,每当更改发生时,事件都会被触发。但是,当您使用InvalidationListener
时,情况并非如此。如果您计划使用InvalidationListener,请记住以下两个规则:
否则,请切换到ChangeListener。