JavaFX Loves Xtend

JavaFX is Java's new rich client platform. With JavaFX it is easy to create modern graphical user interfaces. It has built-in support for visual effects and animations, supports touch-aware devices and it includes an API for data-binding and event propagation. JavaFX is the official successor to Java Swing but it will result in much better looking apps.

Xtend is a statically typed programming language developed at Eclipse. Based on Java, Xtend removes the syntactic noise and adds the missing bits such as type inference, lambda expressions, extension methods or operator overloading. It is compiled to Java code and thus integrates seamlessly with existing libraries and frameworks. Xtend brings the fun back to Java programming without any trade-off in terms of compatibility. Xtend's additional features are a perfect match to the needs of a JavaFX programmer. It will make your code shine the same way as a JavaFX application does. In this article, I will demonstrate that with a couple of examples.
This article relates to Xtend version 2.4.

Event Listeners

Each JavaFX Node has a couple of convenience methods to react on UI events such as mouse clicks, key strokes or multi-touch gestures. In Java a mouse click can for example be handled with the following code:

// Java code
Rectangle node = new Rectangle(); 
node.setOnMouseClicked(new EventHandler<MouseEvent>() { 
   @Override 
   public void handle(MouseEvent event) { 
      System.out.println(event.getButton() + " button clicked");  
   }
});

Let's see how we can improve this code by using Xtend. First of all, Xtend's type inference allows to skip the type when it can be inferred from the context. So the Rectangle construction becomes

val node = new Rectangle

Note that semicolons can be skipped as well as empty parentheses.

Anonymous classes with a single method such as the EventHandler instance is a very common Java idiom. Xtend improves on this significantly by allowing to use lambda expressions instead. The type is inferred from the actual context. In the example the expected type is an EventHandler<MouseEvent>, so the lambda expression is automatically converted to this type:

val node = new Rectangle
node.setOnMouseClicked([ 
   MouseEvent event | println(event.getButton + "button clicked")
])

The part MouseEvent event | denotes the parameter list of the lambda. If the lambda has only one parameter it can be skipped and it will be named it. The parameter type is also inferred from the context, in this case it must be MouseEvent. Like this you can omit it as the receiver of feature call. Without the explicit parameter the code looks like this:

val node = new Rectangle
node.setOnMouseClicked([ 
   println(getButton() + "button clicked")
])

Finally, Xtend has a shortcut syntax for getters and setters: Instead of getButton() you can just write button. Similarly, setOnMouseClicked(arg) becomes onMouseClicked = arg. Once again this is just syntactic sugar. The accessor methods are still called. Our final result reads as:

val node = new Rectangle
node.onMouseClicked = [ 
   println(button + "button clicked")
]

This snippet of Xtend code is completely equivalent to the Java version above. It is a lot shorter, but it is still easier to understand as all the syntactic noise has been cut out. Still everything is type-safe and well defined.

The With Operator vs Generated Builder Classes

Sometimes a JavaFX object has quite a lot of parameters to be customized. Instead of implementing a new constructor with a huge number of parameters for each use case, the JavaFX developers generated builder classes with fluent APIs, e.g.

   // Java code
   Rectangle rectangle = RectangleBuilder.create() 
      .width(80)
      .height(30)
      .fill(Color.BLUE)
      .stroke(Color.RED)
      .strokeWidth(1.2)
      .arcWidth(12)
      .arcHeight(12)
      .build(); 

In Xtend the generated boilerplate is not needed at all because there is the with operator => that binds the preceding argument to the parameter of a lambda expression and executes the latter:

val rectangle = new Rectangle => [ 
   Rectangle r |
   r.setWidth(80) 
   r.setHeight(30) 
   r.setFill(Color::BLUE)
   r.setStroke(Color::RED)
   r.setStrokeWidth(1.2)
   r.setArcWidth(12)
   r.setArcHeight(12)
]

Using the implicit it as closure parameter and the sugared setter call as explained above our code becomes:

val rectangle = new Rectangle => [ 
   width = 80 
   height = 30 
   fill = Color::BLUE 
   stroke = Color::RED 
   strokeWidth = 1.2 
   arcWidth = 12
   arcHeight = 12
]

Note that the Xtend code is shorter than the Java builder syntax even though it does not require an extra builder class. In a similar way, the with operator facilitates the creation of object trees, e.g. subtrees of JavaFX's scene-graph.

Extension Methods For High-Level Property Binding

In JavaFX, the value of a property can be derived from other properties by means of another fluent API for the calculation. E.g. given two DoubleProperties a and b, you can bind a property average which will be automatically updated when a or b change:

// Java code
average.bind(a.add(b).divide(2));

When these calculations get more sophisticated and you do a lot of them the code becomes very unreadable. This is the right moment to think about overloaded operator extensions for JavaFX's DoubleExpressions. The code to overload an operator looks like this:

def static operator_plus(DoubleExpression a, ObservableNumberValue b) {
   a.add(b)	
}

You would usually collect all such overloading methods in a class DoublePropertyExtensions and make them available using a static extension import whenever you need them. The above Java example becomes as simple as

import static extension my.company.DoublePropertyExtensions.*
//...
average << a + b / 2 

Extension methods and operator overloading can also be used to add the missing APIs for geometry calculation, e.g. to apply a Transform to a Point3D or to accumulate Transforms. For detail please see the article on "Accumulating JavaFX Transforms With Xtend".

Declaring Properties

JavaFX properties are usually wrapped in getters and setters to comply with the JavaBeans specification. For a lazily created DoubleProperty radius the recommended code looks like this:

//Java code
public class Balloon {
   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 is a lot of code for a simple property and a perfect use case for a new language feature of Xtend: With Active Annotations you can manipulate how Xtend is compiled to Java. In our case, you could implement an Active Annotation @FXBean that describes the code pattern above. If an Xtend class is annotated as @FXBean, this pattern is expanded for all its fields in the generated Java code automatically by the compiler. So the Xtend version becomes

@FXBean 
class Balloon {
   double radius = 20 
   //...
}

The exemplary code for @FXBean can be found here. Note that Active Annotations are loaded from the classpath of the project at compile time, thus can reside in the same workspace as the client code. They neither have to be deployed to be used nor shipped with the application code. For more infos on Active Annotations please refer to the other article in the newsletter.

I hope I could convince you that JavaFX and Xtend make a perfect couple. If you want to stay up-to-date with our work on Xtend, join us on Twitter or ask your questions in our Google Group.

About the Author
Jan Koehnlein

Jan Koehnlein
itemis