Skip to content

Creating a Val or Var Instance

JordanMartinez edited this page Mar 10, 2016 · 2 revisions

How to create a Val/Var instance?

Val and Var are created via their respective class' static factory methods. Some are shown below, grouped by the kind of Val or Var they create.

Val

  • Constant Val: Create a Val whose value never changes:
  • Val.constant(T value)
  • Convert an ObservableValue into a Val
  • Val.wrap(ObservableValue dependency)
  • Apply some modifications to the dependencies before storing the returned modified value in the Val object:
  • Val.animate(ObservableValue, BiFunction) - Create a Val that gradually transitions to the value of the given ObservableValue every time that vaue changes.
  • Val.combine(ObservableValue<T> dependencies..., resolver) - Create a Val that watches the given dependencies and when any one of them changes, run the resolver's code (which depends on the number of dependencies: Function, BiFunction, TriFunction, etc.) and set this Val to the returned result
  • Val.conditionOn(ObservableValue<T> observable, ObservableValue<Boolean> condition)
  • Val.create(Supplier, ObservableValue... dependencies) - Create a Val whose value gets updated whenever one of the dependencies' values changes.
  • etc.
  • Create a Val with suspendability:
  • Val.suspendable(ObservableValue<T> observable)

Var

  • Create a regular Var object:
  • Var.newSimpleVar(T initialValue)
  • Create a mapped Var that is bound bidirectionally to another property. When that property changes, this Var is set to the result of the applying the propToVarMapper function to its value and vice versa:
  • Var.mapBidirectionally(ObservableValue<T> observable, Function propToVarMapper, Function varToPropMapper)/
  • Convert a primitive property frequently used by JavaFX Controls to a Var (see below for why this is necessary):
  • Var.integerVar()
  • Var.doubleVar()
  • Var.floatVar()
  • Var.longVar()
  • Create a Var with suspendability:
  • Var.suspendable()

The JavaFX Property<Number> implementation issue:

You'll notice that Var has "wrap" methods for primitive-type properties. You may ask, "Why is that needed?" Let's look at the following code:

IntegerProperty simpleIntProp = new SimpleIntegerProperty(4);
Var<Integer> varIntProp = Var.newSimpleVar(6);

// Surprise! Your IDE will flag this as a compile error!
varIntProp.bind(simpleIntProp);

Why the error you ask? It's an implementation problem affecting all Number-related types (Integer, Double, Float, and Long). Each implements Property<Number>, not Property<Primitive_Type>. The only way around this is to use Var<Number>, which is rather annoying.

IntegerProperty simpleIntProp = new SimpleIntegerProperty(4);
Var<Number> varNumProp = Var.newSimpleVar(6);

// no issue now...
varNumProp.bind(simpleIntProp);

// ...but to get the integer value, now we need to call
int integerValue = varNumProp.getValue().intValue();

Why is this a problem? Because all the objects provided in the JavaFX framework that use primitive number type properties (ScrollPane, Circle, etc.) will need to work around this issue. So, to workaround this, Tomas provided the wrap methods for primitive number types:

// Code found in JavaFX provided items like Circle
IntegerProperty anIntProp = new SimpleIntegerProperty(4);

Var<Integer> varIntProp = Var.newSimpleVar(6);

// so a faster way is to use the "integerVar()" method
Var<Integer> anIntPropAsVar = Var.integerVar(anIntProp);
varIntProp.bind(anIntPropAsVar);

Binding Bidirectionally workaround

Additionally, using Val<Integer> will not work well when binding bidirectionally. To work around this, one can use a simulated recursion-updating mechanism:

IntegerProperty intProp = new SimpleIntegerProperty(4);
Var<Integer> intVar = Var.newSimpleVar(4);

// use EventStreams to feed the two properties values into one another when any one of them changes.
Subscription simulatedBidirectionalBinding = Subscription.multi(
    Val.wrap(intProp).values().feedTo(intVar),
    intVar.values().feedTo(intProp)
);