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 signatureFXObservableUtil#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() ) { /* ... */ } } );
I dont get it, what’s the usecase for this feature, other than internal improvements of efxclipse plugin?
Did you ever had to attach listeners to observables? So this small utility helps you to write better code
Pingback: JavaFX links of the week, February 7 | JavaFX News, Demos and Insight // FX Experience