Skip to content

An Intro to Val and Var

JordanMartinez edited this page Mar 10, 2016 · 1 revision

Val and Var - ReactFX Properties

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).

What is it?

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) - Since EventStreams 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.

Why use Vals and Vars instead of their base property classes?

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

Sources

See these links for clarification and a more in-depth explanation: