Creating JavaFX Properties

In my last article I gave an overview of the general functionality of JavaFX properties, this time I will explain how you can use JavaFX properties in your own classes.

Let us assume we want to implement a class Balloon with two properties: the radius, a double value, and the color which is of type javafx.scene.paint.Color. According to the conventions for JavaFX properties, the class needs to have these methods:

public class Balloon {
    public double getRadius() {...}
    public void setRadius(double newRadius) {...}
    public DoubleProperty radiusProperty() {...}

    public Color getColor() {...}
    public void setColor(Color newColor) {...}
    public ObjectProperty<Color> colorProperty() {...}
}

Simple Implementation

The simplest implementation of this class will just create two private fields for the properties. The class would look like this:

public class Balloon {
    private final DoubleProperty radius = new DoubleProperty(this, "radius", 20);
    public double getRadius() { return radius.get(); }
    public void setRadius(double newRadius) { radius.set(newRadius); }
    public DoubleProperty radiusProperty() { return radius; }

    private final ObjectProperty<Color> color = new ObjectProperty<Color>(this, "color", Color.RED);
    public Color getColor() { return color.get(); }
    public void setColor(Color newColor) { color.set(newColor); }
    public ObjectProperty<Color> colorProperty() { return color; }
}

Three arguments are passed to the constructores in the example above, a reference to the containing Object, the name of the property, and the initial value of the properties (20 and Color.RED). The first two values will be returned, if a user calls getBean() or getName() on the property. These values are not required by the properties themselves, they can be constructed without. But it is recommended to set these values for all public properties, because you never know if a user needs them. Only for local usage, for example private fields, you can safely omit them.

The getter and setter forward calls to the getter and setter of the property and the methods radiusProperty() and colorProperty() return the created properties.

This is the simplest implementation possible, I believe, but it has a disadvantage. It requires to create an Object for each property, even if advanced features like bindings are not used. This may not sound too bad, but one has to keep in mind that this is one Object per property. If your class contains a number of properties and you create several instances, the number of created property objects can become huge.

To reduce the memory consumption, we can introduce lazy initialization. Lazy initialization means, we do not create the properties immediately, but only once they are needed. So far I know of two patterns, which are general enough to be reused for all kinds of properties.

Rarely used properties

The first pattern works well for properties that are rarely used. With that I mean, that they usually keep their initial default value and a developer would rarely use the advanced features of bindings. For such a property, the following implementation can be used:

private static final double DEFAULT_RADIUS = 20;
private DoubleProperty radius;
public double getRadius() {
    return (radius != null)? radius.get() : DEFAULT_RADIUS;
}
public void setRadius(double value) {
    if ((radius != null) || (value != DEFAULT_RADIUS)) {
        radiusProperty().set(value);
    }
}
public DoubleProperty radiusProperty() {
    if (radius == null) {
        radius = DoubleProperty(this, "radius", DEFAULT_RADIUS);
    }
    return radius;
}

One of the main differences is that the field radius is not final anymore. It is initialized with null and we will only create an object and assign it to radius once it is needed. That is lazy initialization.

In this approach, the DoubleProperty needs to be available if either the user calls radiusProperty() to use the advanced features or if the value is set to something different than the default value. (In the example above the default value is stored in DEFAULT_RADIUS.)

The getter changed only slightly compared to the simple approach. We first check if a DoubleProperty was created for radius (radius != null) and if it was, we call the getter of the property as we did in the simple approach. If radius is still null, we know for sure, that it still carries the DEFAULT_VALUE, because setting another value or binding it to something else would have created the property. Therefore we return DEFAULT_VALUE, if radius is still null.

The DoubleProperty is created in the radiusProperty() method. First it checks, if it has been created already. If not, a new DoubleProperty is created and assigned to radius. From that moment on, the field radius will not change anymore, it will always point to the DoubleProperty we just created.

The setter calls radiusProperty() to create the property if needed and calls the setter on that object. The if-statement makes sure, we do not create an object if the setter is called with the default value.

The main advantage of this pattern is, that it has almost no performance penalty, only a minor increase in static footprint. One might wonder, if it really has any substantial impact, because already changing the value of the property causes the instantiation of the DoubleProperty. But to our own surprise it works better than we expected. My impression is, that in general the more properties a class has, the more properties are usually not changed. For example, if a class has only two properties, usually both are changed. But if a class has 20 properties, it is very likely that a number of them are never touched. For that reason, I would always recommend this approach for objects with many properties.

Usually no advanced features used

The second pattern works well for properties, which advanced features are usually not used. Here is the implementation for the field radius.

private DoubleProperty radius;
private double _radius = 20;
public double getRadius() {
    return (radius != null)? radius.get() : _radius;
}
public void setRadius(double value) {
    if (radius != null) {
        radius.set(value);
    } else {
        _radius = value;
    }
}
public DoubleProperty radiusProperty() {
    if (radius == null) {
        radius = DoubleProperty(this, "radius", _radius);
    }
    return radius;
}

This pattern is very similar to the one above. Again we do not create radius upfront, but wait until it is needed. With this approach, the DoubleProperty is only needed, if a user wants to use the advanced features of JavaFX properties. In other words, just setting a different value will not cause the creation of the DoubleProperty.

For this approach to work, we introduce a second field _radius, that contains the value until the DoubleProperty radius is created. The getter and setter first check, if radius was already created and use then either the DoubleProperty radius or the double _radius. The method radiusProperty() works exactly as in the previous approach.

This approach is kind of a mixture between a typical implementation for traditional Java Beans and the simple implementation of JavaFX properties. The main advantage is, that creating the DoubleProperty object can be avoided even more often than in the approach above, but this comes with the price of an additional field. It is a trade-off. If you expect that a property is usually changed, but the advanced features like attaching a listener or binding the property are usually not used, this approach is recommended.