JavaFX for Experienced Programmers

What JavaFX is not

JavaFX is not Java. It is a brand new language that builds on top of the JVM and the runtime libraries.  There is a new syntax, and a lot of new syntactic sugar aimed that reducing the quantity of code needed to

  • create and animate user interfaces
  • update user interfaces as model data changes
  • update model data from the user interface

A Quick Taste of JavaFX for the Impatient

// hello.fx (.fx is the only allowed file extension, hello.jfx will not work)
//import javafx.ex.swing.*;
import javafx.ext.swing.SwingFrame;
import javafx.ext.swing.Label;
import javafx.scene.paint.Color;java.lang.System.out.println(”Step 1″);
SwingFrame {
  title: “Chui’s Counterpoint”
  visible: true
  content: Label {
    text: “Hello World!”
  }
}
java.lang.System.out.println(”Step 2″);

Building and running is straightforward

javafxc hello.fx
javafx hello

This yields a standard ugly Swing Frame with “Hello World” label.

The important take-away is

  1. There is no main loop
  2. Although the code looks declarative, it is actually run imperatively, statement by statement.
  3. Instantiation of classes does not require a “new” keyword
  4. There is no need to assign the newly instantiated class to a variable
  5. There is no messy getContentPane().add(myJLabel)

Syntax Nit-picking

It appears to me that spaces are optional in JavaFX. For instance:

content:Label{text:”Hello”width:50}

is valid syntax. There is usually some measure of benefit in enforcing readability in a language. Ignoring spaces also lead to some potential disambiguation issues. For instance, FORTRAN “for” loops are difficult to parse. do 10 i = 1, 5 could potentially mean assignment do10i=1.5 if the comma was mistyped, and replaced with a period.

I found the syntax makes it difficult to decide where to place the closing braces. For example, should we prefer the following - where the closing brace lines up with the content attribute:

  content: Label {
    text: “Hello World!”
  }

or should we prefer this - where the closing brace lines up with the constructor?

  content:
    Label {
      text: “Hello World!”
      color: Color.web(”#336699″)
    }

This leaves a hanging indent, similar to Python. There’s nothing wrong with hanging indents, but a language should make it easy for teams to just stick with one system.

I suspect the latter style is more useful, since it makes it easier to move elements around).  You will need to customize your editor to indent code upon carriage return following an attribute.

Java Problem #1: Too many statements are required to initialize a user interface

JavaFX solution: Declarative User Interfaces using Object Initializers

A common problem encountered developing user interfaces in Java, is the number of statements it takes to accomplish particular tasks. For instance, to add a JLabel in swing would require three statements:

JLabel myJLabel = new JLabel();
myJLabel.setText(”Hello”);
frame.getContentPane().add(myJLabel)

C# exponents would probably note the how JavaFX also uses Object Initializer syntax to reduce the amount of code written. The following is the C# equivalent (from msdn)

StudentName student = new StudentName { FirstName = “Craig”, LastName = “Playstead”, ID = 116 };

So the Java example above could be rewritten in JavaFX as a single expression:

  content: Label {
    text: “Hello World!”
  }

Java Problem #2: Binding user interface to data is not baked into the Java, but requires libraries to implement cooperating classes

User interface components like JTable require a cooperative class to implement a specific Model interface, in order for the user-interface to be able to consume the data. This makes the user-interface king, as it dictates how data is represented internally, otherwise, additional work would be required to write adapters so that data could be adapted to a particular model.

JavaFX solves this by automatically wiring up update handlers using the bind keyword. In the example below, a Text node is bound to myString. When the value of myString is changed by the background thread called timeline, the Text node’s content will be updated automatically.

// bind.fx
import javafx.application.Frame;
import javafx.application.Stage;
import javafx.scene.text.Text;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.lang.Duration;

var myString = “Hello World!”;
Frame {
    width: 150
    height: 50
    visible: true
    stage: Stage {
        content: [
            Text {
                content: bind myString
                x: 0
                y: 10
            }
        ]
    }
}
var timeline = Timeline {
    keyFrames: [
        KeyFrame {
            time: Duration { millis: 1000 }
            action: function() {
                myString = "Goodbye World";
            }
        },
        KeyFrame {
            time: Duration { millis: 2000 }
            action: function() {
               
myString = "Hello World";
            }
        },
    ]
    repeatCount: 3
}
timeline.start();Decompiling bind.class with jad java decompiler yields the following nuggets:

1. The timeline keyframe has a Function instance

new Function0() {
  public void lambda()
  {
     bind.myString.set(”Goodbye World”);
  }
}

2. myString is a static variable in the bind class and it is not strongly typed at all.

public class bind implements Intf, FXObject {
  public static final ObjectVariable myString;
}

The question arises then, how do we integrate ordinary POJOs into JavaFX to make them observable? Perhaps the easiest way is to marshall data as JSON, and then recreate the instances using JavaFX classes. The JavaFX classes could be created automatically via annotations on the original POJO class definitions.

Binding to expressions

JavaFX permits binding to expressions. For instance, we can replace the example above with the following:

// bind.fx
import javafx.application.Frame;
import javafx.application.Stage;
import javafx.scene.text.Text;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.lang.Duration;

var myString = “Hello World!”;
Frame {
    width: 150
    height: 50
    visible: true
    stage: Stage {
        content: [
            Text {
                content: bind "JavaFX says: ".concat(myString)
                x: 0
                y: 10
            }
        ]
    }
}
var timeline = Timeline {
    keyFrames: [
        KeyFrame {
            time: Duration { millis: 1000 }
            action: function() {
                myString = "Goodbye World";
            }
        },
        KeyFrame {
            time: Duration { millis: 2000 }
            action: function() {
               
myString = "Hello World";
            }
        },
    ]
    repeatCount: 3
}
timeline.start();

Running the compiled class file through the Jad decompiler yields this interesting method called getStaticDependents()

protected Location[] getStaticDependents()
{
    return (new Location[] {
        arg$0, bfx$0selector
        // arg$0 is myString, bfx$0selector is “JavaFX says: “

    });
}

public String computeValue()
{
    return bfx$0selector.get() == null ? null : ((String)bfx$0selector.get()).concat((String)arg$0.get());
}

Presumably, the purpose of getStaticDependents is to construct a giant dependency graph so that when one item updates, the dependent values will be recomputed.

 

Java Problem #3. Updating the underlying model from User Interface is not baked into the language

Writing code to update model is repetitive and adds little joy to a developers daily work. In Java, much of the drudgery can be taken out by using Binding frameworks. This usually involves calls to a binding library that converts user-interface values into model values. For instance, “1.25″ has to be parsed to 1.25 and followed by updating the underlying property on the model. There are several competing Binding frameworks, Swing Data Binding aka JGoodies Binding, Beans Binding, JBind, Castor. Some would say that too much effort is being devoted to something so fundamental.

JavaFX’s approach is to get rid of external libraries, and put it in the language instead via the keyword “bind … with inverse”.

// twoway.fx
// demonstrates two way binding
import javafx.ext.swing.SwingFrame;
import javafx.ext.swing.TextField;
class Person
{
    attribute name: String
        on replace oldVal = newVal { java.lang.System.out.println(oldVal.concat(” updated to “).concat(newVal)); }
}
var person = Person { name: “Chui Tey” }
SwingFrame {
    width: 400
    title: “Two way binding”
    visible: true
    content: TextField {
        editable: true
        width: 250
        text: bind person.name with inverse
    }
}

There are two new constructs introduced here.

Firstly, the declaration “with inverse” allows the TextField to update the underlying model. (omitting “with inverse” results in an illogical AssignToBoundException).

Secondly, we have created a trigger, which is an analog of setter() methods in Java. The syntax is a mix of SQL triggers and ruby blocks. It’s not particularly pretty, and I have a dislike of introducing new syntax when a good old lambda would have sufficed. e.g.

class Person
{
    attribute name: String
        function onReplace(oldVal, newVal) { java.lang.System.out.println(oldVal.concat(” updated to “).concat(newVal)); }
}

However, the new syntax allowed slice assignments to be expressed. I have not delved into this, but would have thought it is better to extend the function() syntax to take the slice assignments rather than having a new construct altogether. As an old BASIC programmer, the idea of OPEN FILE FOR READ is anathema to building good libraries, and makes code difficult to introspect.

Unaddressed Issues

While JavaFX is an improvement on Java for building RIA applications, I feel it has not completely addressed the issues of making it easy for designers to create interactive components. For instance, there is no clear path for a programmer to prototype using Swing components, and then a designer replaces the swing buttons, textboxes and listboxes with a totally custom UI, while retaining the original behaviors.

There is also no clear guidance for using custom renderers for plain-old-objects. For example, WPF has a concept of DataObjects, where plain-old-objects are embedded within a UI container, and specialized renderers are registered to render these objects when they are encountered. 

JavaFX is an improvement, but Java could adopt some of the changes too

As a language that is intended to solve some of Java’s deficiency in the construction of user interfaces, binding of data, and change notification, JavaFX has addressed these issues reasonably. However, there is no reason why Java couldn’t adopt Object Initializers as a way to simplify coding.

One Response to “JavaFX for Experienced Programmers”

  1. JavaFX Coverflow Part 1 | Chui’s counterpoint writes:

    [...] been about 2 weeks since my first JavaFX session. My initial distaste for the YAML syntax is largely gone. I have avoided using Netbeans, as vim is sufficient. The absence of closing tags [...]

Leave a Reply