At the heart of JavaFX is its scenegraph, a structure that includes (perhaps many) nodes. The JavaFX rendering engine displays these nodes and ultimately what you see depends on the properties of these nodes.
Properties are oh-so-important. They make a Circle red or a Rectangle 50 pixels wide. They determine the gradient for a background color or whether or not some text includes reflection or a drop shadow effect. You manipulate nodes with layout components—which are themselves nodes—by setting properties such as spacing or alignment. When your application includes animation, JavaFX updates a node’s properties over time—perhaps a node’s position, its rotation, or its opacity.
Before we look at JavaFX properties in detail, let’s review JavaBean properties first. JavaBean properties support encapsulation and a well-defined naming convention. You can create read-write properties, read-only properties, and immutable properties.
For example, here is a simple Person JavaBean with property fullname
. (In fact, once we write the instance variable fullname
, an IDE can generate the getter and setter.) Because we provide both getter and setter for this String property, the property is read-write. A Person object is built with a default constructor.
public class Person { private String fullname = ""; public String getFullname() { return fullname; } public void setFullname(String fullname) { this.fullname = fullname; } @Override public String toString() { return getFullname(); } }
We can use Person as follows.
Person p = new Person(); p.setFullname("Joe Smith"); System.out.println(p); // Joe Smith
Okay, so what are JavaFX properties and how are they different from regular JavaBean properties? JavaFX properties are JavaBean properties on steroids. That is, like JavaBean properties, they support encapsulation, a well-defined naming convention, as well as read-only, read-write, and immutable attributes. But there’s more. To see the difference, let’s create a Person POJO with a JavaFX property.
public class Person { private StringProperty fullname = new SimpleStringProperty(this, "fullname", ""); public final StringProperty fullnameProperty() { return fullname; } public final String getFullname() { return fullname.get(); } public final void setFullname(String fullname) { this.fullname.set(fullname); } @Override public String toString() { return getFullname(); } }
You’ll notice right away that JavaFX properties include additional implementation code. First, the instance variable type is no longer String, but StringProperty, a type that wraps a String value and provides enhanced JavaFX property behaviors and structure, as you will soon see.
Second, conforming to the JavaFX property naming convention, you access a JavaFX property exactly the same as a JavaBean property. This means client code doesn’t change.
// unchanged from JavaBean property Person p = new Person(); p.setFullname("Joe Smith"); System.out.println(p); // Joe Smith
Third, also confirming to the JavaFX property naming convention, you provide access to the Property itself with method propertynameProperty()
. Here is method fullnameProperty()
again.
public final StringProperty fullnameProperty() { return fullname; }
Fourth, JavaFX properties store contextual information. Method getBean()
returns a reference to the enclosing object and getName()
returns the property’s name. Here, we initialize these values in the property’s constructor, along with an empty String to use as its initial value.
private StringProperty fullname = new SimpleStringProperty(this, "fullname", "");
And, we can use these methods as follows.
System.out.println(p.fullnameProperty().getBean()); // returns p and prints 'Joe Smith' System.out.println(p.fullnameProperty().getName()); // prints 'fullname'
As alluded to earlier, JavaFX properties support additional behaviors. JavaFX properties are observable, which means you can attach listeners and define event handlers when a property value changes or becomes invalid. JavaFX properties also support unidirectional and bidirectional binding and unbinding. Binding lets properties participate in dependencies with other property values. Unbinding removes any dependencies. These are powerful constructs that let JavaFX developers write code that easily synchronizes itself based on changing values. These behaviors are important for responsive UIs as well as animation.
Because all JavaFX nodes contain many JavaFX properties, the JavaFX API does not implement properties in the straightforward way I have described. Instead, JavaFX nodes use lazy evaluation for properties. Thus, for any given property, if a node object only gets and sets values, the overhead for that property is equivalent to a regular JavaBean property.
Our Person POJO with its JavaFX property could be re-implemented as follows. Now the actual JavaFX property is created only when the StringProperty object is accessed with fullnameProperty()
.
public class Person { private String _fullname = ""; private StringProperty fullname; public final StringProperty fullnameProperty() { if (fullname == null) { fullname = new SimpleStringProperty( this, "fullname", _fullname); } return fullname; } public final String getFullname() { if (fullname == null) { return _fullname; } else return fullname.get(); } public final void setFullname(String fullname) { if (this.fullname == null) { _fullname = fullname; } else this.fullname.set(fullname); } @Override public String toString() { return getFullname(); } }
Note that our user code only inflates fullname
(that is, invokes the SimpleStringProperty constructor) after the first call to p.fullnameProperty()
, as shown here.
Person p = new Person(); p.setFullname("Joe Smith"); System.out.println(p); // JavaFX Property fullname inflated here: System.out.println(p.fullnameProperty().getBean()); // prints 'Joe Smith' System.out.println(p.fullnameProperty().getName()); // prints 'fullname'
What about JavaFX properties with types other than String? JavaFX supports the following property types.
BooleanProperty, DoubleProperty, FloatProperty, IntegerProperty, LongProperty, ObjectProperty<T>, StringProperty
These are abstract types and are instantiated with SimpleBooleanProperty, SimpleDoubleProperty, and so forth.
We’ll explore examples with JavaFX properties that use binding next.
Pingback: JavaFX links of the week, November 28 // JavaFX News, Demos and Insight // FX Experience
Pingback: Java desktop links of the week, November 28th | Jonathan Giles
Thanks for the explanation.
Some questions though: 2 typical kinds of applications are e.g.:
Use case 1: access an embedded database with JPA
Use case 2: access a remote Web Service with JAX-WS
Case 1:
How should the JPA entities look like? Should the data layer/ business layer contain JavaFX properties (UI layer)? Or should we duplicate the POJOs – once with “normal” properties and once with JavaFX properties? Other solutions?
Case2:
How can we create JavaFX properties from a WSDL (JAXB)?
It would be great if you (or someone else) could provide some examples for these 2 use cases. (Or point me to them if they already exist.)
Currently I still have the feeling that these JavaFX are really a work-around for a shortcoming in the Java language and I don’t see yet how to make them effectivly work with frameworks such as JPA or JAXB without lot of (alomst) duplicated code.
I guess this is another indicator that Java really needs language level support for properties, which is so fundamental to many frameworks.
Pingback: Blog: JavaFX: Properties on Steroids | Oracle | Syngu
Thanks for this post.
I have a question about the encapsulation in the JavaFx properties. If a class featuring a property is supposed to return the property instance through a ‘propertyNameProperty’ method, then how can the integrity of the data in the property be safeguarded? E.g. What would prevent a user of the class Person (illustrated in this post) from assigning an invalid value by accessing the property, and directly setting value to it like:
personObject.fullNameProperty().set("Some Invalid String Value");
In JavaFX properties are used mostly to implement the event driven model at the user interface level. That is to say, the programmer know the property values will be changed, as that is the role of a user interface. Check out the TableView tutorial on Oracle’s site an you’ll see how much it simplifies the programming. On the other hand, if you want to block the re-setting of the property value, you may want to look at the ReadOnlyXxxProperty abstract classes and extend those to accomplish what you want — or ReadOnlyXxxWrapper which is even better.