Using JavaFX's Properties

First a general reminder: Do not get into micro-optimization - It clutters the code for little to no benefit!

So with JavaFX we got a very nice API for writing user interface applications and with Java 8’s Lambda Expressions it is even more fun. Just a single line of code is needed to wire up a new listener:

1
event.resultStatusProperty().addListener(obs -> doMagic());

Now that is not a bad thing at all, but I have seen two common things that developers tend to ignore or do not know:

1. Creating a JavaFX Property

1
2
private final ObjectProperty<ResultStatus> resultStatus =
    new SimpleObjectProperty<>(this, "resultStatus", initialResultStatus);

This is an example of an ObjectProperty, but the other (primitive type) Properties work the same way: As definied in javafx.beans.property.ReadOnlyProperty every Property has a bean and a name as additional, optional attributes.
I often see JavaFX code creating Properties only using the value side, leaving out bean and name. Why should I use it, you ask? Well, let us move on:

2. Using JavaFX Property Listeners

There are two types of a listener that may be added to a JavaFX Property:

  1. InvalidationListener
  2. ChangeListener

I won’t go much into detail discussing the pros and cons of every one, this would be out of scope of this article. Maybe another time.
Besides having different names and different use cases, they have some things in common:

  • They are both functional interfaces, so they may be expressed as Lambdas
  • Both receive an Observable passed on as first argument on each change.

Well, our ObjectProperty for instance and every other JavaFX property is implementing Observable!

Example

Let us assume, we have an application tracking server-side events. There are thousands of them over the course of a day. The server will push new events to our client application. But the event will be in progress and we can not process the event(s) until they are finished.
The naive way of doing this would be to just do this

1
event.resultStatusProperty().addListener(obs -> doMagic());

on every incoming event. Works great. Time to go home.
But since our client application is already struggling by dealing with thousands of events we can actually improve client load by using what I just tried to explain:

Creating the Listener

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
InvalidationListener resultStatusListener = pObservable ->
{
  if (pObservable instanceof ObjectProperty &&
      ((ObjectProperty<?>) pObservable).getBean() instanceof ArchiveEvent)
  {
      ArchiveEvent event = (ArchiveEvent) ((ObjectProperty<?>) pObservable).getBean();
      switch (event.getResultStatus())
      {
          case SUCCESS:
              // TODO
              break;
          case FAILED:
              // TODO
              break;
          case WARNING:
              // TODO
              break;
      }
  }
};

Now we have created THE listener, we just use this one instance on every result property of every incoming event.
And it is quite simple: Every time a property becomes invalid we check if it is an instance of our result property and if the bean has been properly set (instanceof does an implicit null check).

And this is just a small example, you can go further on by not only using the bean but also name of the property.
Yes, you will need a few more lines of code for checking and casting but that saved you a lot of memory if you create/attach serious amounts of listeners.

Comments