|
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.
|
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:
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.
|
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 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
 |
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
 |
?
|
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:
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
 |
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:
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
|
|