ListProperty::bind has different behavior than StringProperty::bind when observables are equal

José Pereda jose.pereda at gluonhq.com
Tue Jun 9 09:23:01 UTC 2020


Hi,

We have come across a possible issue that affects bind() over different
types of properties.
We are not sure if it's a bug or it behaves as it should be. Therefore, we
are reaching out to the community for insight.

See these short code snippets below:

Snippet 1. StringProperty (could be other like IntegerProperty)

StringProperty a = new SimpleStringProperty();
StringProperty b = new SimpleStringProperty();
StringProperty c = new SimpleStringProperty();
c.addListener((obs, ov, nv) -> System.out.println("Change: " + nv));
c.bind(a);
c.bind(b);
b.set("One");
System.out.println("c contains: " + c.get());
====

Snippet 2. ListProperty

ListProperty<String> a = new
SimpleListProperty<>(FXCollections.<String>observableArrayList());
ListProperty<String> b = new
SimpleListProperty<>(FXCollections.<String>observableArrayList());
ListProperty<String> c = new SimpleListProperty<>();
c.addListener((ListChangeListener.Change<? extends String> change) ->
System.out.println("Change: " + change));
c.bind(a);
c.bind(b);
b.add("One");
b.add("Two");
System.out.println("c contains: " + Arrays.toString(c.toArray()));
====

While snippet 1 produces:

Change: One
c contains: One

as expected, snippet 2 produces:

Change: { [] added at 0 }
c contains: []

But the expected result is:

Change: { [] added at 0 }
Change: { [] added at 0 }
Change: { [One] added at 0 }
Change: { [Two] added at 1 }
c contains: [One, Two]

This expected result only happens by manually doing:

c.bind(a);
c.unbind(); // need to explicitly unbind
c.bind(b);

Summing up: when binding a new observable to a bound property, unbind is
required if that property is a ListProperty, but not a StringProperty (or
IntegerProperty).

Explanation

In the ListProperty.bind implementation [1] there is a check, and only when
both current bound observable and new observable are equal, unbind() is
applied.

The equals is implemented in ReadOnlyListProperty::equals [2] and, being a
list, follows the List::equals[3] contract, therefore, it compares by items
of the list.

This is not the case when using other properties like StringPropertyBase or
IntegerPropertyBase, where bind() is implemented in the same way [4], but
equals now uses Object::equals[5], so the comparison is now done with ==,
instead of comparing by the content of each property.

Is this a bug? Should StringProperty, IntegerProperty override equals to
compare by content and not by object, like ListProperty does? Should
ListProperty use == operator instead of equals?

This difference in behavior should be at least documented, shouldn't it?


Thanks for any input on this,
Jose


[1]
https://github.com/openjdk/jfx/blob/22d4343fe8563c2931910b98e8f18c6fd4a48f05/modules/javafx.base/src/main/java/javafx/beans/property/ListPropertyBase.java#L268
[2]
https://github.com/openjdk/jfx/blob/22d4343fe8563c2931910b98e8f18c6fd4a48f05/modules/javafx.base/src/main/java/javafx/beans/property/ReadOnlyListProperty.java#L110
[3]
https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/List.java#L542
[4]
https://github.com/openjdk/jfx/blob/22d4343fe8563c2931910b98e8f18c6fd4a48f05/modules/javafx.base/src/main/java/javafx/beans/property/StringPropertyBase.java#L161
[5]
https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/lang/Object.java#L150

--


More information about the openjfx-dev mailing list