ObservableValue
Holds a value of type V, which can be observed using InvalidationListeners. When the value changes, the InvalidationListeners are informed via InvalidationListener.invalidated.
Property is a notable sub-interface, whose value is mutable. They have ChangeListeners (similar to InvalidationListener, but are also sent the old value
and newValue
.
Note. It is common to name an ObservableValue xxxProperty
, even though it isn't really a Property. For example, suppose we have a Rectangle class, with left
, right
and width
ObservableValues. We could choose to store left
and right
as Properties
, with width
as a BinaryFunction. Or we could store left
and width
as Properties
, with right
as a BinaryFunction. No matter which we choose, it is acceptable to name them leftProperty
, rightProperty
and widthProperty
, despite one of them not inheriting from Property.
Atomic Updates
An atomic update, guarantees that two or more related properties are never in an inconsistent state, where the value of one property is out-of-date, because it hasn't been updated yet.
Consider SingleSelectionModel
used by ListView
, TreeView
etc. It has two properties selectedIndex
and selectedItem
. When we change one of these properties, the other property must change before any of your listeners fire, your listener will see an inconsistent state.
Glok doesn't support atomic updates, but does offer some help, by having two sets of listeners. Those added via addBindListener and addBindChangeListener fire before those added by addListener and addChangeListener.
Bound properties, and LazyObservableValue always use addBindListener and addBindChangeListener. In general, your code should use addListener and addChangeListener, and therefore the property values will be consistent.
However, if your (regular) listeners change another property, beware that other listeners of the original property can see inconsistent values. It will see the new value of the original property, but the value of the other property will depend on the order the listeners were added. If it were added before, it will see the old (inconsistent) value.
In my experience, the lack of atomic updates isn't an issue. YMMV. None of my applications directly use addBindListener nor addBindChangeListener, but they do make extensive use of bound properties and LazyObservableValues (such as UnaryFunction, BinaryFunction etc).
Class Diagram
╭╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
┆ Observable ┆
┆ addListener() ┆
┆ removeListener() ┆
╰╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
△
┌────────────────────────┴──┬────────────────┐
╭╌╌╌╌╌╌╌╌╌╌╌╌┴╌╌╌╌╌╌╌╌╌╌╌╌╮ ╭╌╌╌╌╌╌╌╌┴╌╌╌╌╌╮ ╭╌╌╌╌╌╌┴╌╌╌╌╌╌╮
┆ ObservableValue ┆ ┆ObservableList┆ ┆ObservableSet┆
┆ val value : V ┆ ╰╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯ ╰╌╌╌╌╌╌╌╌╌╌╌╌╌╯
┆ addChangeListener() ┆
┆ removeChangeListener() ┆
┆ ┆
╰╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
△ ╭╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
├─────────────────────────────────────────┐ ┆InvalidationListener┆
╭╌╌╌╌╌╌╌╌╌┴╌╌╌╌╌╌╌╌╌╌╮ ┌──────────┴──────────┐ ┆ invalidated() ┆
┆ ReadOnlyProperty ┆ │ ObservableValueBase │◇────┤ ┆
┆ bean : Any? ┆ │ listeners │ ╰╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
┆ beanName : String ┆ │ changeListeners │ ╭╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
┆ ┆ │ │◇────┤ChangeListener┆
╰╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯ └──────────┬──────────┘ ┆ changed() ┆
△ │ ┆ ┆
┌──────────────────┴────┐ │ ╰╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
┏━━━━━━━━━━━┷━━━━━━━━━━━━━┓ ╭╌╌╌╌╌╌╌┴╌╌╌╌╌╌╌╌╮ ╭╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮ │
┃ ReadOnlyPropertyWrapper ┃ ┆ Property ┆ ┆ BidirectionalBind ┆ │
┃ listeners ┃ ┆ var value : V ┆◀───┤ propertyA ┆ │
┃ changeListeners ┃ ┆ ┆◀───┤ propertyB ┆ │
┃ ┃ ┆ ┆ ┆ unbind() ┆ │
┗━━━━━━━━━━━━━━━━━━━━━━━━━┛ ╰╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯ ╰╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯ │
△ │
│ │
│ │
┌────────────────────────┬───┴───────────────────────────────────┴───┐
╭╌╌╌╌╌╌╌┴╌╌╌╌╌╌╌╌╌╌╮ ┌───────┴────────┐ ┌───────────┴──────────┐
┆ StylableProperty ┆ │ PropertyBase │ │ LazyObservableValue │
┆ kClass() ┆ │ │ │ invalidate() │
┆ style() ┆ │ │ │ eval() │
┆ revert() ┆ └────────────────┘ │ addDependant() │
╰╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯ △ │ removeDependant() │
△ (Used by Themes) │ └──────────────────────┘
│ │ △
│ │ ┌─────────────────┴────────────────────┐
│ ┏━━━━━━━━┷━━━━━━━━┓ ┏━━━━━━━━┷━━━━━━┓ ┏━━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━┷━━━━━━┓
│ ┃ SimpleProperty ┃ ┃ UnaryFunction ┃ ┃ BinaryFunction ┃ ┃ TernaryFunction ┃
│ ┃ ┃ ┃ argA ┃ ┃ argA ┃ ┃ argA ┃
│ ┃ ┃ ┃ lambda ┃ ┃ argB ┃ ┃ argB ┃
│ ┗━━━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━┛ ┃ lambda ┃ ┃ argC ┃
│ △ ┗━━━━━━━━━━━━━━━━┛ ┃ lambda ┃
│ ┌──────────────────┘ ┗━━━━━━━━━━━━━━━━━┛
┏━━━━━━━┷━━━━━┷━━━━━━━━┓
┃SimpleStylableProperty┃
┃ ┃
┃ ┃
┗━━━━━━━━━━━━━━━━━━━━━━┛
Created with Blokart
Inheritors
Functions
Identical to addChangeListener, but these listeners are guaranteed to fire BEFORE other listeners. These should be used for the sole purpose of updating single ObservableValues which are dependent on this ObservableValue. This helps (but doesn't guarantee) that properties change atomically. i.e. when one property changes, a related property also changes before other (regular) listeners fire. Therefore, the (regular) listeners cannot read inconsistent values.
Identical to addListener, but these listeners are guaranteed to fire BEFORE regular listeners. These should be used for the sole purpose of updating other ObservableValues which are dependent on this Observable.
Converts an ObservableValue, with a non-nullable to value, to an ObservableValue whose value IS nullable.
Converts an ObservableValue, with a nullable to value, to an ObservableValue whose value is NOT nullable, by supplying a defaultValue.