JavaFX Properties

JavaFX 2.0 properties build upon the popular Java Beans pattern and enhance them with some useful and extremely powerful features. In this article, I will take a closer look at the usage of JavaFX properties. It is the first in a series of articles, which will explain JavaFX properties in detail.

JavaFX Beans

Let us assume we have a class Point with two double properties x and y. Both properties are JavaFX properties. The class would be defined as follows:

public class Point {
    public double getX() {...}
    public void setX(double x) {...}
    public DoubleProperty xProperty() {...}

    public double getY() {...}
    public void setY(double y) {...}
    public DoubleProperty yProperty() {...}
}

The getter and setter are no surprise. They are defined exactly as getters and setters are defined for JavaBeans. What is new are the other methods xProperty() and yProperty().  Both method names are created by concatenating the property name x respectively y and the String “Property”. The return type is an instance of the new class DoubleProperty, which encapsulates all of the new features. There are classes like this for the most common primitive types (double, float, int, long, and boolean), for Strings and for arbitrary Objects.

Class DoubleProperty

Let us take a closer look at DoubleProperty, the other properties are defined equivalent. The following list shows the property specific methods. DoubleProperty contains more methods, most of them related to the fluent API to create bindings. I will cover them in another article, for now we will just focus on the methods below.

public class DoubleProperty extends ... {
    public Object getBean() {...}
    public String getName() {...}

    public double get() {...}
    public void set(double v) {...} 

    public Double getValue() {...}
    public void setValue(Number value) {...}

    public void addListener(ChangeListener<? super Number> listener) {...}
    public void removeListener(ChangeListener<? super Number> listener) {...}

    public void addListener(InvalidationListener<? super Number> listener) {...}
    public void removeListener(InvalidationListener<? super Number> listener) {...}

    public void bind(ObservableValue<? extends Number> observable) {...}
    public void unbind() {...}
    public boolean isBound() {...}

    public void bindBidirectional(Property<Number> other) {...}
    public void unbindBidirectional(Property<Number> other) {...}
}

Getting the context

The first two methods getBean() and getName() return information about the context of the property. The method getBean() returns the object that contains the current property, the method getName() returns the name of it. For example, if you define a Point p1, the expression p1.xProperty().getBean() would return the containing Point p1 and p1.xProperty().getName() would return “x”. These two methods may look redundant, but there are situations, when you have to deal with instances of DoubleProperty directly without knowing the context, and in these situations getBean() and getName() come in very handy.

Getters and Setters

The next two methods get() and set() allow to get and set the value of a property. They are equivalent to the getter and setter defined in the containing object, i.e. p1.xProperty().get() is equivalent to p1.getX().

There is another pair of getter and setter (getValue() and setValue()), which use boxed types. These methods are mainly intended for code that needs to deal with all kinds of properties in a generic way. If you can, it is always recommended to use the primitive versions get() and set().

Attaching Listeners

The next four methods allow to attach and detach change-listeners and invalidation-listeners. The differences between change events and invalidation events can be very important. More about listeners is planned soon.

Bindings

A property can be bound to a target. This means, that the value of the property is synchronized with the target, i.e. it is guaranteed that the bound property and the target always contain the same value. Two kinds of bindings exist, unidirectional bindings and bidirectional bindings. A unidirectional binding works only in one direction, changes of the target are propagated to the property. A bidirectional binding works in both direction, changes are propagated from the target to the property, but also in the reverse direction from the property to the target.

A unidirectional binding imposes some limitations on the bound property. Once a property is bound, it is not possible to change its value in any other way, but by changes of the target. It is not possible to set the value with the setter, it would throw an Exception. And there can be only one unidirectional binding for a property at a time. The main advantage is, that you can bind a property to complex expression with unidirectional bindings. You can define these expressions with the Binding API. Every time one of the operands of such an expression changes, the property will be updated with the new result automatically. For example if you bind p1.xProperty() to the expression a + b, p1.getX() will always return the sum of a and b. If a or b change, p1.getX() will automatically return the new sum. With the method bind() you can define a unidirectional binding, unbind() removes the binding and isBound() checks if the property is bound at the moment.

A bidirectional binding on the other hand can only be established between properties of the same type. If one of the properties changes, the other property is updated, too. The usage is much more relaxed, you can still use the setter and it is even possible to define numerous bidirectional bindings for a single property. With bindBidirectional() a bidirectional binding can be defined and with unbindBidirectional() it can be removed.