e(fx)clipse runtime library – Dealing with listeners on JavaFX-Observables


Now that the e(fx)clipse runtime code base is available to any application developer (see this blog post). It is time to explore all the cool stuff it provides for Java(FX) application development.

In the next few weeks I’ll blog about new stuff and old stuff you can make use by just adding a dependency on one of our libraries. I’ll use maven in the samples because that’s what I’m most used to but IIRC things will work for gradle as well.

In this first post I’d like to draw your attention to an Utility-Class (org.eclipse.fx.core.observable.FXObservableUtil) I just checked in a few minutes ago into the repository helping you to write fewer and less error prone code:

FXObservableUtil.onChange for ObservableValue & Property

Let’s take a look at a first code-pattern, I’ve noticed frequently in my JavaFX code-bases:

private StringProperty p = ...;

void attachHandler() {
   p.addListener( this::handle );
}

void detachHandler() {
   p.removeListener( this::handle );
}

void handle(Observable ob, String ol, String ne) {
   System.out.println( ne );
}

The code above has the following problems:

  • I have to declare a method with 3 arguments although I only make use of the new value
  • I have created a huuuge listener leak (Arghhh!)

Let’s improve the situation by using one of our core-libraries org.eclipse.fx.core. Just the following to your maven build:

...
<repositories>
  ...
  <repository>
    <name>BestSolution e(fx)clipse snapshots</name>
    <id>efxclipse-snapshots</id>
    <url>http://maven.bestsolution.at/efxclipse-snapshots/</url>
    <snapshots>
      <enabled>true</enabled>
    </snapshots>
  </repository>
</repositories>
...
<dependencies>
  ...
  <dependency>
    <groupId>at.bestsolution.efxclipse.rt</groupId>
    <artifactId>org.eclipse.fx.core</artifactId>
    <version>3.0.0-SNAPSHOT</version>
  </dependency>
</dependencies>

And now we can change our code using an Utility-Class named org.eclipse.fx.core.observable.FXObservableUtil.

import org.eclipse.fx.core.observable.FXObservableUtil;

private StringProperty p = ...;
private Subscription s;

void attachHandler() {
   s = FXObservableUtil.onChange( p, this::handle );
}

void detachHandler() {
   if( s != null ) {
     s.dispose();
     s = null;
   }
}

void handle(Observable ob, String ol, String ne) {
   System.out.println( ne );
}

But we can improve the code even more because:

  • We can add static imports
  • Instead of using FXObservableUtil#onChange(ObservableValue<T>, ChangeListener<T>) we can use the overloaded method with the signature FXObservableUtil#onChange(ObservableValue<T>, Consumer<T>)
import static org.eclipse.fx.core.observable.FXObservableUtil.*;

private StringProperty p = ...;
private Subscription s;

void attachHandler() {
   s = onChange( p, System.out::println );
}

void detachHandler() {
   if( s != null ) {
     s.dispose();
     s = null;
   }
}

FXObservableUtil.onChange for ObservableList

In general the JavaFX APIs is designed very well to work with @FunctionalInterface types but IMHO there’s one big mistake that has happened when we look at the Observable-API who strikes back in case of ObservableList.

The class hierarchy in JavaFX looks like this:

+ Observable
  - addListener( InvalidationListener listener ) : void
  + ObservableValue
    - addListener( ChangeListener<? super T> listener )
  + ObservableList
    - addListener( ListChangeListener<? super E> listener )

The overloading of addListener() in case of ObservableValue is fine as ChangeListener#changed(ObservableValue<? extends T> , T , T ) requires 3 arguments wherease InvalidationListener#invalidated(Observable) takes only one.

In case of ObservableList unfortunately ListChangeListener#onChanged( Change<? extends E> c) like InvalidationListener#invalidated(Observable) accepts one argument. Because of this it’s impossible to benefit of the automatic type inference done by the compiler to remove boilerplate code for lambda expressions and you need to fill in the correct type in the lambdas argument section:

ObservableList<String> l = ...

l.addListener( (Change<? extends String> c) -> 
  { while( c.next() ) { /* ... */ } } );
// although you really wanted to write
l.addListener( c -> { while( c.next() ) { /* ... */ } } );

To make this usecase less type heavy FXObservableUtil distinguishes between

  • Invalidation: FXObservableUtil.onInvalidate(Observable,InvalidationListener)
  • Change: FXObservableUtil.onChange(ObservableList<E>,ListChangeListener<? super E>)
import static org.eclipse.fx.core.observable.FXObservableUtil.*;

ObservableList<String> l = ...
Subscription s = 
  onChange( l, c -> { while( c.next() ) { /* ... */ } } );
Advertisements
This entry was posted in e(fx)clipse, Uncategorized. Bookmark the permalink.

3 Responses to e(fx)clipse runtime library – Dealing with listeners on JavaFX-Observables

  1. Nzt says:

    I dont get it, what’s the usecase for this feature, other than internal improvements of efxclipse plugin?

  2. Pingback: JavaFX links of the week, February 7 | JavaFX News, Demos and Insight // FX Experience

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s