Разницу можно увидеть, посмотрев на исходный код StringPropertyBase
и ObjectPropertyBase
- в частности, их set
методов.
StringPropertyBase
@Override
public void set(String newValue) {
if (isBound()) {
throw new java.lang.RuntimeException((getBean() != null && getName() != null ?
getBean().getClass().getSimpleName() + "." + getName() + " : ": "") + "A bound value cannot be set.");
}
if ((value == null)? newValue != null : !value.equals(newValue)) {
value = newValue;
markInvalid();
}
}
ObjectPropertyBase
@Override
public void set(T newValue) {
if (isBound()) {
throw new java.lang.RuntimeException((getBean() != null && getName() != null ?
getBean().getClass().getSimpleName() + "." + getName() + " : ": "") + "A bound value cannot be set.");
}
if (value != newValue) {
value = newValue;
markInvalid();
}
}
Обратите внимание на разницу в том, как они проверяют, равно ли новое значение старому значению?Класс StringPropertyBase
проверяет, используя Object.equals
, тогда как класс ObjectPropertyBase
использует равенство ссылок (==
/ !=
).
Я не могу точно ответить , почему эта разница существует, но я могу рискнуть предположить: ObjectProperty
может содержать что угодно , и, следовательно, есть потенциал дляObject.equals
быть дорогим;например, при использовании List
или Set
.При кодировании StringPropertyBase
я полагаю, что они решили, что потенциал отсутствует, что семантика String
равенства важнее, или и то, и другое.Может быть больше / больше причин, почему они сделали то, что сделали, но, поскольку я не участвовал в разработке, я не знаю о них.
Интересно, если вы посмотрите, как они обращаются со слушателями(com.sun.javafx.binding.ExpressionHelper
) вы увидите, что они проверяют равенство, используя Object.equals
.Эта проверка на равенство выполняется только в том случае, если в настоящее время зарегистрировано ChangeListener
с, вероятно, для поддержки отложенной оценки при отсутствии ChangeListener
с.
Если новые и старые значения равны equals
, то ChangeListener
с не уведомляются.Однако это не мешает уведомлять InvalidationListener
s .Таким образом, ваш ObservableList
сгенерирует изменение обновления, потому что этот механизм основан на InvalidationListener
с, а не ChangeListener
с.
Вот соответствующий исходный код:
ExpressionHelper$Generic.fireValueChangedEvent
@Override
protected void fireValueChangedEvent() {
final InvalidationListener[] curInvalidationList = invalidationListeners;
final int curInvalidationSize = invalidationSize;
final ChangeListener<? super T>[] curChangeList = changeListeners;
final int curChangeSize = changeSize;
try {
locked = true;
for (int i = 0; i < curInvalidationSize; i++) {
try {
curInvalidationList[i].invalidated(observable);
} catch (Exception e) {
Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
}
}
if (curChangeSize > 0) {
final T oldValue = currentValue;
currentValue = observable.getValue();
final boolean changed = (currentValue == null)? (oldValue != null) : !currentValue.equals(oldValue);
if (changed) {
for (int i = 0; i < curChangeSize; i++) {
try {
curChangeList[i].changed(observable, oldValue, currentValue);
} catch (Exception e) {
Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
}
}
}
}
} finally {
locked = false;
}
}
И вы можете увидеть это поведение в следующем коде:
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
public class Main {
public static void main(String[] args) {
ObjectProperty<String> property = new SimpleObjectProperty<>("Hello, World!");
property.addListener(obs -> System.out.printf("Property invalidated: %s%n", property.get()));
property.addListener((obs, ov, nv) -> System.out.printf("Property changed: %s -> %s%n", ov, nv));
property.get(); // ensure valid
property.set(new String("Hello, World!")); // must not use interned String
property.set("Goodbye, World!");
}
}
Вывод:
Property invalidated: Hello, World!
Property invalidated: Goodbye, World!
Property changed: Hello, World! -> Goodbye, World!