-
Notifications
You must be signed in to change notification settings - Fork 48
An Intro to Val and Var
The goal of this page is to give a brief summary of the blog posts that Tomas wrote (see bottom for links and their clarifications to what is written on this page).
As a base, think of Val<T> (value) as a ReadOnlyObjectProperty<T> and Var (variable) as a SimpleObjectProperty<T>.
Now, let's add a few methods to them that deal with situations where their value might be null.
-
Optional<T>-like methods: isPresent()isEmpty()ifPresent(Consumer)orElse(T otherValue)orElse(ObservableValue otherProperty)orElseConst(T constantValue)- Other methods dealing with getting a value that might be
null: getOrElse(T defaultValue)getOrSupply(Supplier suppliedValue)getOrThrow()
Now, let's add two characteristics to these properties: laziness and suspendability.
- Laziness: they only observe their dependencies when something else is subscribed to them (aka Cold Variables).
- Suspendability: their value can be changed without notifying their listeners until a more apt time.
Now, let's add some manipulation methods that are useful for chaining multiple Val/Vars together (only some are shown):
-
animate(BiFunction, Interpolator)- SinceEventStreams don't have a counterpart, see this post for an explanation. conditionOn(ObservableValue<Boolean> condition)filter(Predicate)map(Function)flatMap(Function)
Now, let's add a few convenience methods to get EventStreams for their value invalidations and changes:
invalidations()values()
Congrats! You've now mentally built the concept of Val and Var.
Convenience methods, less boiler-plate, and more readable code is a huge reason. However, there is another reason: notification reliability, Val and Var handle recursive changes better than their counterparts.
For example, the following code...
IntegerProperty p = new SimpleIntegerProperty(0);
p.addListener((obs, old, val) -> {
if(val.intValue() > 0) {
p.set(val.intValue() - 1);
}
});
p.addListener((obs, old, val) -> System.out.println(old + " -> " + val));
p.set(2);should output:
// initial call to set value to 2 => sets value to 2 => notify listeners
0 -> 2
// initial call triggers subcall: set value to 2-1 => set value to 1 => notify listeners
2 -> 1
// subcall triggers subcall: set value to 1-1 => set value to 0 => notify listeners
1 -> 0
but in actually, it will output:
1 -> 0
2 -> 0
0 -> 0
Using Val and Var will lead to a more reliable output:
0 -> 1
1 -> 0
See these links for clarification and a more in-depth explanation:
- Monadic Operations (ops that deal with
null-values) -
How to ensure consistency of your observable state - Note: the package described here was deprecated and its content was moved into
ValandVar - Val<T>: a better ObservableValue