-
Notifications
You must be signed in to change notification settings - Fork 47
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
/Var
s together (only some are shown):
-
animate(BiFunction, Interpolator)
- SinceEventStream
s 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 EventStream
s 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
Val
andVar
- Val<T>: a better ObservableValue