The Wayback Machine - https://web.archive.org/web/20090130125107/http://java.sun.com:80/developer/technicalArticles/scripting/javafxpart1/
Sun Java Solaris Communities My SDN Account Join SDN

Article

Learning JavaFX Script, Part 1: An Introduction to JavaFX Script for Java Programmers

 
By Robert Eckstein and the Authors of the JavaFX Programming Language Reference, July 2007  

Articles Index

NOTE: See Learning the JavaFX Script Programming Language instead. This article is based on the interpreter version of the JavaFX Script Programming Language, which has been replaced by the compiler version. Samples will no longer work as expected.

The JavaFX Script programming language (hereinafter referred to as JavaFX) is a declarative, statically typed scripting language from Sun Microsystems, Inc. As mentioned on the Open JavaFX (OpenJFX) web site, JavaFX technology has a wealth of features, including the ability to make direct calls to Java technology APIs. Because JavaFX Script is statically typed, it also has the same code structuring, reuse, and encapsulation features -- such as packages, classes, inheritance, and separate compilation and deployment units -- that make it possible for your to create and maintain very large programs using Java technology.

This series of three articles will help you get started with the JavaFX programming language. Part 1 of this series is an introduction to the JavaFX programming language, targeted to those who are already familiar with Java technology and the basics of scripting languages. Parts 2 and 3 of the series show how to use JavaFX technology to connect to a remote server using technologies such as Remote Method Invocation (RMI) and the Java API for XML Web Services (JAX-WS).

The JavaFX Pad Application

If you have a Java Runtime Environment (JRE) on your system, the easiest way to get started with JavaFX technology is to fire up the Java Web Start-enabled demonstration program, JavaFX Pad. Once you start the application, you should see a screen similar to what appears in Figure 1.

Figure 1. The JavaFX Pad Application Running on Microsoft Windows Vista, JDK 6.
Figure 1. The JavaFX Pad Application Running on Microsoft Windows Vista, JDK 6.
 

JavaFX Pad starts with a default application already loaded, which it immediately executes. However, you can also cut and paste JavaFX source code from this article over the sample, viewing any modifications. In addition, you can save and load the JavaFX source examples to your local disk. The JavaFX Pad application is a great way to see exactly what you're doing at runtime, modifying changes as you go, and instantaneously seeing the results.

JavaFX Technology: A Statically Typed Language

The JavaFX programming language is a scripting language with static typing. What exactly does this mean? Consider the following:

var myVariable = "Hello";
 

This declaration, similar to what you may find in JavaScript technology, creates a variable called myVariable and assigns it the string value Hello. However, after declaring the variable, let's try to assign it something other than a string:

myVariable = 12345;
 

Because the code does not use quotation marks around 12345, this variable is now being assigned an integer instead of a string. In JavaScript technology, dynamically retyping the variable will work fine. However, a statically typed language such as JavaFX will not allow this. This is because myVariable was initially declared as a String type, and the code later tries to reassign it as an integer. With JavaFX, a variable that is declared as a String must remain a String.

In fact, if you enter those two lines of code into the JavaFX Pad demo, you'll immediately see an error at the bottom of the window, as shown in Figure 2.

Figure 2. Statically Typed Variables Cannot Change Type in JavaFX Technology.
Figure 2. Statically Typed Variables Cannot Change Type in JavaFX Technology.
 
JavaFX Technology: A Declarative Scripting Language

JavaFX technology is also a declarative scripting language. But what exactly does declarative mean? To answer this question, consider this Hello World program from the OpenJFX web site:

class HelloWorldModel {
    attribute saying: String;
}

var model = HelloWorldModel {
    saying: "Hello World"
};

var win = Frame {
    title: bind "{model.saying} JavaFX"
    width: 200
    content: TextField {
        value: bind model.saying
    }
    visible: true
};
 

Most compiled languages, including the Java programming language, are considered imperative programming languages. Among other things, this means that they rely on an entry point, such as the main() method in Java technology, from which they instantiate other classes or fields or dispose of resources based on variables or program state. To stretch this example somewhat, you can say that an imperative programming language determines its path of execution "formulaically" at runtime. This formula may result in the same path with each execution, but the language still determines its path of execution at runtime.

Note, however, that JavaFX programs, such as the Hello World example shown earlier, do not contain a main() method. Instead, the scripting engine reads the entire program just before execution, then allows the interpreter to take whatever steps are necessary to run the program correctly. Put more precisely, everything that the scripting engine needs is declared before execution begins, and the engine decides what to do with all the declarations given to achieve the stated goals.

Using System.out.println() With JavaFX Pad

As we'll see shortly, JavaFX has the ability to call traditional Java libraries. However, if you wish to use System.out.println() with the JavaFX Pad application, you must enable console support. You can do this as follows:

  • If you are using Microsoft Windows XP or Vista, click on the Java icon in the Control Panel, select the Advanced tab, then from the Java Console item, select Show Console.

  • If you are running Solaris, click on the Java icon in Preferences tab, select the Advanced tab, then from the Java Console item, select Show Console. If you do not have a Java icon in the Preferences tab, run the ControlPanel application (or jcontrol) in the bin directory of your Java distribution.

  • If you are running Linux, look for an application called ControlPanel (or jcontrol) inside the bin directory of your Java distribution. Run it, then click on the Java icon in Preferences tab, select the Advanced tab, then from the Java Console item, select Show Console.

  • If you are using Mac OS X, open the Java Preferences application under /Applications/Utilities/Java/[Java version]/. Then select the Advanced tab, and from the Java Console item, select Show Console. Note: If Java Web Start fails to start after making any changes to the Java Preferences, and you're running on an Intel Mac, try opening the file Library/Caches/Java/deployment.properties under your home directory and changing all osarch values from i386 back to ppc.

In JavaFX Pad, disable the Run Automatically setting in the Run menu and clear the Java Console just before manually running any JavaFX application. To manually run an application, use the Run menu item in the Run menu of the JavaFX Pad application.

Figure 3 shows JavaFX Pad running with the console open on an Intel-based Macintosh. Figure 4 shows JavaFX Pad running on OpenSolaris.

Figure 3. JavaFX Pad Running With a Java Console on Mac OS X With JDK 1.5.0_07.
Figure 3. JavaFX Pad Running With a Java Console on Mac OS X With JDK 1.5.0_07.
 
Figure 4. JavaFX Pad Running With a Java Console on OpenSolaris with JDK 6.
Figure 4. JavaFX Pad Running With a Java Console on OpenSolaris with JDK 6.
 

Finally, if you are embedding variables within strings in JavaFX, which you would commonly do with the System.out.println() method in the Java programming language, note that the proper JavaFX syntax is:

    import java.lang.System;

    System.out.println("Text {variable} and more text");
 

This differs from the Java language syntax:

    import java.lang.System;

    System.out.println("Text " + variable + " and more text");
 
Getting to Know JavaFX Technology

This section will discuss the basics of JavaFX technology. Most of this information comes directly from the official JavaFX Programming Language Reference, although the authors of this article have modified the content for Java programmers.

Primitives

The JavaFX programming language provides only four primitive types: String, Boolean, Number, and Integer. Note that, unlike the Java programming language, primitives are written with an initial capital letter. Table 1 shows the types within the JavaFX interpreter as well as the Java objects they map to.

Table 1. Primitive Mappings for JavaFX
JavaFX Technology Primitive
Representative Java Technology Primitive or Class
String
java.lang.String
Boolean
java.lang.Boolean
Number
java.lang.Number
Integer
byte, short, int, long, java.math.BigInteger
 

Note: For simplicity, Integer represents both small and large numbers for which Java programmers may use different primitive types, such as short or long. Floating-point numbers, such as float or double in Java technology, are represented by the Number type.

Because Java objects represent these primitives, you can call the existing Java methods on each of these types.

var s:String = "Hello";
s.toUpperCase();      // String method that yields "HELLO";
s.substring(1);       // String method that yields "ello";

var n:Number = 1.5;
n.intValue();         // Number method that yields integer 1
(1.5).intValue();     // Number method that yields integer 1

var b:Boolean = true;
b instanceof Boolean; // Boolean method that yields true
 

If you'd like to see the results for themselves, wrap each of the expressions about in a System.out.println() statement, and be sure to import java.lang.System at the top. Also, if you get an error about an incompatible type, just add a null return at the end of the script for now. For example:

import java.lang.System;

var s:String = "Hello";
System.out.println(s.toUpperCase());      // String method that yields "HELLO";
System.out.println(s.substring(1));       // String method that yields "ello";

var n:Number = 1.5;
System.out.println(n.intValue());         // Number method that yields integer 1
System.out.println((1.5).intValue());     // Number method that yields integer 1

var b:Boolean = true;
System.out.println(b instanceof Boolean); // Boolean method that yields true

return null;                              // Final node returned for JavaFX Pad display
 

Again, note the use of the var keyword: Although not used in Java technology, var is used in JavaFX and other scripting languages to declare a new variable. Because JavaFX is a statically typed language, you may specify the type of a variable in its declaration, or the JavaFX interpreter will attempt to infer the variable's type from its use. For example, all three of the following are valid in JavaFX:

var s:String;
var s:String = "A New String";
var s = "A New String";
 

The first and second declarations formally assign the String type to the variable, whereas the third infers it as a String from the initializer on the right side of the equals (=) sign. To put this more formally, JavaFX variable declarations can be expressed this way:

var variableName [: typeName] [? | + | *] [= initializer];
 

The question mark, addition sign, and asterisk are called cardinality operators. You may be familiar with these terms if you've used expression languages. You can use one of these three operators to indicate the cardinality (amount of member) of the variable, as Table 2 indicates.

Table 2: JavaFX Cardinality Operators
Operator
Meaning
?
Optional (For example, it can be null.)
+
One or more
*
Zero or more
 

Here is an example:

var nums:Number* = [5, 9, 13];
 

This example declares a new variable named nums whose value is defined to consist of zero or more instances of type Number and whose initial value is three numbers: 5, 9, and 13.

The typeName, cardinality operators, and initializer portions of the declaration are optional, so the following is equivalent to the previous example:

var nums = [5, 9, 13];
 

Literals

In JavaFX technology, a literal character string is specified with single quotation marks or with double quotation marks:

var s = 'Hello';
var s = "Hello";
 

As the authors mentioned earlier, variables and even entire JavaFX expressions may be embedded using curly braces ({}):

var name = 'Joe';
var s = "Hello {name}"; // s = 'Hello Joe'
 

The embedded expression may itself contain quoted strings, which in turn may contain further embedded expressions:

var answer = true;
var s = "The answer is {if answer then "Yes" else "No"}";
    // s = 'The answer is Yes'
 

Finally, unlike Java technology, String literals set in double quotation marks can contain new lines:

var s = "This
         contains
         new lines";
 

Arrays and List Comprehensions

You likely noticed earlier that the cardinality operators create arrays. Arrays in JavaFX are denoted with square brackets and commas. As in Java, the elements of a JavaFX array must all be of the same type.

var weekdays = ["Mon","Tue","Wed","Thur","Fri"];
var days = [weekdays, ["Sat","Sun"]];
 

Arrays represent sequences of objects. In JavaFX, arrays are not themselves objects. In addition, expressions that produce nested arrays -- as in the initialization of the second variable days in the previous code example -- are automatically flattened.

days == ["Mon","Tue","Wed","Thur","Fri","Sat","Sun"];
     // returns true
 

Note that if you execute System.out.println(days), you'll see only the first element of the array. Use an array index to obtain the remaining elements. Also, if you specify an index that does not exist, you will not get an ArrayIndexOutOfBoundsException, as in Java, but instead simply a zero.

You can use the sizeof operator to obtain the current size of an array:

var n = sizeof days;     // n = 7
 

There is also a shorthand notation using two periods (..) to represent arrays whose elements form an arithmetic series. For example, the following will create an array with 100 elements:

var oneToAHundred = [1..100];
var arraySize = sizeof oneToAHundred;   // size == 100
 

JavaFX technology also supports insert and delete statements when working with arrays. Here are examples of inserting values into an array.

var x = [1,2,3];
insert 12 into x;                 // yields [1,2,3,12]
insert 10 as first into x;        // yields [10,1,2,3,12]
insert [99,100] as last into x;   // yields [10,1,2,3,12,99,100]
 

In addition to into, you can also use the before and after keywords, as follows:

var x = [1,2,3];
insert 10 after x[. == 3];        // yields [1,2,3,10]
insert 12 before x[1];            // yields [1,12,2,3,10]
insert 13 after x[. == 2];        // yields [1, 12, 2, 13, 3, 10];
 

If some of the expressions inside the square brackets look odd, don't worry. They are actually Xquery-Update (similar to XPath) predicates. In this case, the period inside the brackets does not refer to an index but instead to the value of the index. JavaFX technology will test each one in the array until the expression is true, then apply the insert. For example, the last statement will iterate through each of the values of the array, inserting the number 13 after it finds an array value that equals 2, which is the third element in the array. At that point, it stops.

The delete statement works much the same way. Note that if an expression in square brackets is omitted, the entire array is cleared.

var x = [1,2,3];
insert 10 into x;          // yields [1,2,3,10]
insert 12 before x[1];     // yields [1,12,2,3,10]
delete x[. == 12];         // yields [1,2,3,10]
delete x[. >= 3];          // yields [1,2]
insert 5 after x[. == 1];  // yields [1,5,2];
insert 13 as first into x; // yields [13, 1, 5, 2];
delete x;                  // clears the array and yields []
 

Finally, you can perform more complex queries on arrays by using the select and for each operators. These are known as list comprehensions. Here is a simple example of using select:

var a:Integer* = select n*n from n in [1..10];
    //  yields [1,4,9,16,25,36,49,64,81,100]
 

This is shorthand for saying "Loop through each number in the [1..10] array, assigning it to the local variable n. Then for each n, create a new element n squared and add it to the array of Integers a."

You can also add a filter using the following:

var a:Integer* = select n*n from n in [1..10] where (n%2 == 0);
    //  yields [4,16,36,64,100]
 

This changes the definition to the following: "Loop through each number in the [1..10] array, assigning it to the local variable n, but only if that number divided by 2 has a remainder of zero -- in other words, it's an even number. Then for each resulting n, create a new element n squared and add it to the array of Integers a."

Finally, you can add multiple lists to the selection as well:

var a:Integer* = select n*m from n in [1..4], m in [100,200] where (n%2 == 0);
    //  yields [200, 400, 400, 800]
 

This is, in effect, a loop within a loop, formally creating a Cartesian product. You start by obtaining the first valid n, which is 2, and multiplying it by the first valid m, 100. Then the same 2 is multiplied by the next valid m, 200. Then you reiterate over the list of valid m's (100 and 200) using the next valid n, which is 4, yielding 400 and 800. At this point, the selection ends, and you are presented with your final array, which is placed in the array a.

The same thing can be expressed using the foreach operator:

var a:Integer* =
    foreach(n in [1..4], m in [100,200] where (n%2 == 0) )
            n*m;      // also yields [200, 400, 400, 800]
 
Formatting

The format as operator supports several formatting directives, as shown in Table 3.

Table 3: JavaFX Formatting Directives
Directive
Format Used Is Class...
Formatting directive starts with %
java.util.Formatter
Expression is Number
java.text.DecimalFormat
Expression is java.util.Date
java.text.SimpleDateFormat
 

Here are some examples:

import java.util.Date;

100.896 format as <<%f>>; // yields '100.896000'
31.intValue() format as <<%02X>>;
    // yields '1F'
var d = new Date();
d format as <<yyyy-MM-dd'T'HH:mm:ss.SSSZ>>;
    // yields '2005-10-31T08:04:31.323-0800'
0.00123 format as <<00.###E0>>;
    // yields '12.3E-4'
 

Note the use of the French quotation marks, or guillemets, (<< >>) in this example. JavaFX technology treats as an identifier any sequence of characters, including white space, that is contained in French quotation marks. This allows you to use JavaFX keywords or other normally illegal identifiers as class, variable, function, or attribute names. Here's an example:

var <<while>> = 100;
 

This feature makes it possible to call Java methods whose names are the same as JavaFX keywords, as in this example:

import javax.swing.JTextArea;

var textArea = new JTextArea();
textArea.<<insert>>("Hello", 0);
 
Declaring Classes

The JavaFX syntax for specifying a class is the class keyword followed by the class name, optionally the extends keyword, and a comma-separated list of the names of base classes. Note that, unlike Java, you can extend more than one class with JavaFX technology. This is followed by an open curly brace; a list of attributes, functions, and operations that each end in a semicolon (;) and a closing curly brace. Here is an example:

class Person {

    attribute name: String;
    attribute parent: Person;
    attribute children: Person*;

    function getFamilyIncome(): Number;
    function getNumberOfChildren(): Number;

    operation marry(spouse: Person): Boolean;
}
 

Attributes, Functions, and Operations

Let's take a closer look at the three types of class member declarations. Attributes are declared using the attribute keyword followed by the attribute's name, a colon (:), the attribute's type, optionally a cardinality specification, as well as an optional inverse clause. If a cardinality is used, the attribute is often known as a multivalued attribute.

Put more formally, an attribute uses the following syntax:

attribute AttributeName [: AttributeType] [? | + | *] [inverse ClassName.InverseAttributeName];
 

The optional inverse clause at the end specifies a bidirectional relationship to another attribute. If an inverse clause is present, JavaFX technology will automatically update the attribute specified in the inverse clause -- by using an insert or delete or replace, depending on the kind of update and cardinalities of the attributes -- whenever this attribute is modified.

Functions represent a pure functional subset of the JavaFX programming language. In other words, the body of a function may only contain a series of variable declarations and a return statement. This makes them perfect for attribute accessors – –getters– – and simple mathematical procedures. Here are some example functions:

function z(a,b) {
    var x = a + b;
    var y = a - b;
    return sq(x) / sq (y);
}

function sq(n) {return n * n; }
 

Operations in JavaFX are declared using the operation keyword. Unlike functions, operations may contain any number of statements such as conditional statements, looping statements, try and catch statements, and so on. This makes them more akin to class methods in Java technology. When declaring the body, the name of the operation is given, followed by the input variables in parentheses, a colon, and the return variable type. Here is an example:

operation substring(s:String, n:Number): String {
    try {
        return s.substring(n);
    } catch (e:StringIndexOutOfBoundsException) {
        throw "sorry, index out of bounds";
    }
}
 

Initializing Declared Attributes

As with functions and procedures, initial values for attributes are declared outside the class definition. The initializers are evaluated in the order that the attributes are specified in the class declaration in the context of the newly created object:

import java.lang.System;

class X {
    attribute a: Number;
    attribute b: Number;
}

attribute X.a = 10;
attribute X.b = -1;

var x = new X();
System.out.println(x.a); // prints 10
System.out.println(x.b); // prints -1
 

JavaFX objects may also be initialized using a declarative syntax consisting of the class name followed by a curly brace-delimited list of attribute initializers. Each initializer consists of the attribute name followed by a colon, followed by an expression that defines its value. Note that the new keyword is omitted. Here is an equivalent example:

var myXClass = X {
    a: 10
    b: -1
};
 

Declarative syntax is frequently used in JavaFX technology. However, Java object allocation syntax is also supported. In the case of Java classes, you can pass arguments to the class's constructor as in Java technology, or you can use the declarative syntax:

import java.util.Date;
import java.lang.System;

var date1 = new Date(95, 4, 23);    // call a Java constructor
var date2 = Date {  // create the same date as an object literal
    month: 4
    date: 23
    year: 95
};

System.out.println(date1 == date2);   // prints true
 

Function and Operation Definitions

Unlike Java methods, the bodies of all member functions and operations are defined outside of the class declaration. This syntax may look a bit strange to a Java programmer at first, but it is relatively simple to follow. With JavaFX technology, the function or operation name is preceded by the class that it belongs to and a period. The return value can be listed after the function or operation name and is preceded by a colon. Parameters can be included in the signature parentheses. They are separated by a comma and follow the name:type syntax shown earlier for operations. Here is an example:

operation Person.marry(spouse: Person): Boolean {
    //  Body of operation
}
 

Parameters and return types are required in the declaration of operations and functions within the class declaration. However, they may be omitted in their outside definitions. Hence, you can just as easily say this:

operation Person.marry() {
    //  Body of operation
}
 
Triggers

Unlike Java technology, JavaFX classes do not have constructors, and JavaFX attributes typically do not have "setter" methods. Instead, JavaFX technology provides SQL-like triggers that allow you to handle data-modification events. These triggers use the trigger keyword.

Object Creation Triggers

You can trigger an action in the context of a newly created object by specifying a creation trigger:

import java.lang.System;

class X {
     attribute nums: Number*;
}

trigger on new X {
     insert [3,4] into this.nums;
}

var x = new X();
System.out.println(x.nums == [3,4]); // prints true
 

This example defines a trigger that will be executed whenever a new instance of the X class is created. In this case, it inserts two numbers into the nums attribute. Note the use of the this keyword to indicate the current object in the context of the trigger.

Insert Triggers

You can also trigger an action whenever an element is inserted into a multivalued attribute:

import java.lang.System;

class X {
     attribute nums: Number*;
}

trigger on insert num into X.nums {
     System.out.println("just inserted {num} into X.nums at position {indexof num}");
}

var x = new X();
insert 12 into x.nums;
    // prints just inserted 12 into X.nums at position 0
insert 13 into x.nums;
    // prints just inserted 13 into X.nums at position 1
 

Delete Triggers

In the same manner, you can trigger an action whenever an element is deleted from a multivalued attribute:

import java.lang.System;

class X {
     attribute nums: Number*;
}

trigger on delete num from X.nums {
     System.out.println("just deleted {num} from X.nums at position {indexof num}");
}

var x = X {
     nums: [12, 13]
};

delete x.nums[1];
    // prints just deleted 13 from X.nums at position 1
delete x.nums[0];
    // prints just deleted 12 from X.nums at position 0
 

Replace Triggers

Finally, you can perform an action whenever the value of an attribute is replaced. In the following example, oldValue and newValue are arbitrary variable names that reference the previous and current values of the element being replaced. You are free to chose other variable names.

import java.lang.System;

class X {
     attribute nums: Number*;
     attribute num: Number?;
}

trigger on X.nums[oldValue] = newValue {
     System.out.println("X.nums: replaced {oldValue} with {newValue} at position {indexof newValue}");
}

trigger on X.num[oldValue] = newValue {
     System.out.println("X.num: replaced {oldValue} with {newValue}");
}

var x = X {
     nums: [12, 13]
     num: 100
};

x.nums[1] = 5;
    // prints replaced 13 with 5 at position 1 in X.nums
x.num = 3;
    // prints X.num: replaced 100 with 3
x.num = null;
    // prints X.num: replaced 3 with null
 
Statements

JavaFX technology allows several statements that are similar but not identical to their counterparts in Java technology. This section outlines how they differ.

If Statement

The JavaFX if statement is like the one in Java technology except that curly braces are required:

if (condition1) {
    System.out.println("Condition 1");
} else if (condition2) {
    System.out.println("Condition2");
} else {
    System.out.println("not Condition 1 or Condition 2");
}
 

While Statement

The JavaFX while statement must also use curly braces around the body:

var i = 0;
while (i < 10) {
    if (i > 5) {
       break;
    }
    System.out.println("i = {i}");
    i += 1;
}
 

Try, Catch, and Throw Statements

The JavaFX try and catch statement is like that in Java technology but with JavaFX variable declaration syntax. Note that in JavaFX technology, any object can be thrown and caught, not just those that extend java.lang.Throwable.

try {
   throw "Hello";
} catch (s:String) {
   System.out.println("caught a String: {s}");
} catch (any) {
   System.out.println("caught something not a String: {any}");
} finally {
   System.out.println("finally...");
}
 

For Statement

The header of the JavaFX for statement uses the same syntax as the foreach list-comprehension operator discussed earlier. The body of the statement is executed for each element generated by the list comprehension. Here is an example:

for (i in [0..10]) {
     System.out.println("i = {i}");
}

// print only the even numbers using a filter
for (i in [0..10] where (i%2 == 0) ) {
     System.out.println("i = {i}");
}

// print only the odd numbers using a range expression
for (i in [1,3..10]) {
     System.out.println("i = {i}");
}

// print the cartesian product
for (i in [0..10], j in [0..10]) {
     System.out.println(i);
     System.out.println(j);
}
 

Return Statement

The JavaFX return statement is identical to that in Java technology:

operation add(x, y) {
    return x + y;
}
 

Break and Continue Statements

The JavaFX break and continue statements are like those in Java technology, except that labels are not supported. As in Java programming, break and continue must appear inside the body of a while or for statement.

operation foo() {
   for (i in [0..10]) {
       if (i > 5) {
           break;
       }
       if (i % 2 == 0) {
           continue;
       }
       System.out.println(i);
   }
}

operation bar() {
    var i = 0;
    while (i < 10) {
        if (i > 5) {
            break;
        }
        if (i % 2 == 0) {
            continue;
        }
        System.out.println(i);
        i += 1;
    }
}
 

Do and Do Later Statement

The JavaFX do statement allows you to execute a block of JavaFX code. However, the do body always executes in a background thread. Normally, JavaFX code executes in the AWT Event Dispatch Thread (EDT) -- only code contained in the body of a do statement is allowed to execute in another thread. Consider this example:

import java.net.URL;
import java.lang.StringBuffer;
import java.lang.System;
import java.io.InputStreamReader;
import java.io.BufferedReader;

// in the AWT Event Dispatch Thread (EDT)
var result = new StringBuffer();

do {
    // now in a background thread
     var url = new URL("http://www.foo.com/abc.xml");
     var is = url.openStream();
     var reader = new BufferedReader(new InputStreamReader(is));
     var line;
     while (true) {
          line = reader.readLine();
          if (line == null) {
               break;
          }
          result.append(line);
          result.append("\n");
     }
}

// now back in the EDT
System.out.println("result = {result}");
 

The code that is executing in the EDT blocks during the execution of the body of the do statement. However, a new event dispatch loop is created on the stack while waiting for the background thread to complete. As a result, graphical user interface (GUI) events continue to be processed while the do statement is executing.

The do statement has a second form, do later, that allows for asynchronous execution of its body in the EDT rather than synchronous execution in a background thread, similar to the functionality provided by java.awt.EventQueue.invokeLater(). Here's an example:

import java.lang.System;
var saying1 = "Hello World!";
var saying2 = "Goodbye Cruel World!";
do later {
     System.out.println(saying1);
}
System.out.println(saying2);
 

Running this code produces the following output:

Goodbye Cruel World!
Hello World!
 
Incremental Evaluation

Incremental evaluation is one of JavaFX technology's most exciting features: It allows the programmer to define complex, dynamic GUIs declaratively. In JavaFX technology, attribute initializers can be incrementally evaluated with the bind operator. Once bound, these attributes behave much like cells in a spreadsheet that contain formulas rather than literal values. Whenever any of the objects referenced by the right side of the initializer expression changes, the left side -- the attribute's value -- is automatically updated.

Here is a simple example:

import java.lang.System;

class X {
    attribute a: Number;
    attribute b: Number;
}

var x1 = X {
    a: 1
    b: 2
};

var x2 = X {
    a: x1.a           // nonincremental
    b: bind x1.b      // incremental
};

System.out.println(x2.a); // prints 1
System.out.println(x2.b); // prints 2

x1.a = 5;
x1.b = 5;

System.out.println(x2.a); // prints 1
System.out.println(x2.b); // prints 5
 

In this example, the b attribute of x2 is bound to the b attribute of x1. This means that whenever the b attribute of x1 is updated, the b attribute of x2 will be updated as well.

Note that the body of a function is always incrementally evaluated without requiring the bind operator, but the body of an operation is not. Unlike a function, changes to local variables inside an operation do not trigger incremental evaluation. Incremental evaluation is not performed inside the body of an operation except for expressions explicitly prefixed by bind.

You can modify the example to use lazy incremental evaluation as well. The binding here does not take effect until the attribute value is accessed the first time.

import java.lang.System;

class X {
    attribute a: Number;
}

var x1 = X {
    a: 1
};

var x2 = X {
    a: bind lazy x1.a
    // no value is assigned yet
};

System.out.println(x2.a);
// Now the value is accessed, so x2.a is set to 1
 

The lazy incremental evaluation feature is often used to handle recursive data structures, such as trees and graphs.

Conclusion

This article has presented a simple introduction to the JavaFX platform. Parts 2 and 3 of this article will discuss ways to handle client-server communication with JavaFX technology-based GUIs.

For More Information