Basic Types
Warning: JavaFX has no assert support yet! This reference just acts as if it did. (more)
assert(true); // assertions must be true
assert(not false); // "not" instead of "!"
assert(!false); // "!" not defined
assert(true and true); // logical and, like Java's && (more)
assert(true or false); // logical or, like Java's || (more)
assert(true instanceof java.lang.Boolean);
assert(false instanceof java.lang.Boolean);
assert(1 == 1); // equal
assert(1 != 2); // not equal
assert(2 > 1); // greater than
assert(2 >= 1); // greater or equal
assert(1 < 2); // less than
assert(1 <= 2); // less or equal
assert("foo" == "foo"); // == and != compare Strings (unlike Java) (more)
assert("foo" != "bar");
def a = 1; assert(a == 1); // def defines variables that can not be overwritten
a = 2; // must not modify def variables
var b = 1; assert(b == 1); // var declares a regular variable
b = 2; assert(b == 2);
var a, b; // one var or def per variable allowed
var c = 1; c = "foo"; // c is Integer because of the initial assignment (more)
var d : Integer = 1; d = 2; // explicit type declaration
var e : Boolean = true; e = 5; // incompatible type
var f : Object = 1; f = "foo"; // Object allows all values (more)
def g : String = null; // reference types can be null
def h : Integer = null; // ..but not built-ins like Integer(more)
var x = 0;
var y = 2 on replace { x++; }; // on replace clause
assert(x == 1); // called on initial assignment
y = 0; assert(x == 2); // and after additional assignments
assert(5 < 10); // decimal Integer literal
assert(26 == 0x1a); // hexadecimal
assert(10 == 012); // octal
assert(1 instanceof Integer);
var x : Integer; assert(x == 0); // default value 0
var y : Integer = null; // Integer can not be null
assert(1 + 2 == 3); // addition
assert(1 - 2 == -1); // subtraction
assert(6 * 7 == 42); // multiplication
assert(24 / 6 == 4); // division
assert(25 / 6 == 4);
assert(25 mod 6 == 3); // reminder
assert(-(1+1) == -2); // negation
var a = 1; assert(a++ == 1); assert(a == 2); // Post-increment
var b = 1; assert(++b == 2); assert(b == 2); // Pre-increment
var c = 1; assert(c-- == 1); assert(c == 0); // Post-decrement
var d = 1; assert(--d == 0); assert(d == 0); // Pre-decrement
var e = 1; e += 3; assert(e == 4); // add then assign
var f = 2; f -= 1; assert(f == 1); // subtract then assign
var g = 2; g *= 3; assert(g == 6); // multiply then assign
var h = 8; h /= 2; assert(h == 4); // divide then assign
assert(12.5 == 25.0/2.0); // Number / float literal
assert(0.75 == .75); // Omitting zero
assert(1.25e2 == 125); // exponential notation
assert(184e-2 == 0.184e1);
assert(0.75 instanceof java.lang.Number);
assert(0.75 instanceof java.lang.Float);
var x : Number; assert(x == 0.0); // default value 0.0
var y : Number = null; // Number can not be null
assert(2.5 + 4.5 == 7.0); // addition
assert(0.75 - 2 == -1.25); // subtraction
assert(0.5 * 7.0 == 3.5); // multiplication
assert(12.0 / 8 == 1.5); // division
var a = 1.5; assert(a++ == 1.5); assert(a == 2.5); // Post-increment
var b = 1.5; assert(++b == 2.5); assert(b == 2.5); // Pre-increment
var c = 1.5; assert(c-- == 1.5); assert(c == 0.5); // Post-decrement
var d = 1.5; assert(--d == 0.5); assert(d == 0.5); // Pre-decrement
var e = 1.5; e += 3.5; assert(e == 5.0); // add then assign
var f = 2.5; f -= 1; assert(f == 1.5); // subtract then assign
var g = 2.5; g *= 3.0; assert(g == 7.5); // multiply then assign
var h = 8.6; h /= 2; assert(h == 4.3); // divide then assign
assert(2.5 + 2 + 5.5 == 10); // type conversion Integer <-> Number
assert("foo" == 'foo'); // double-quote and single-quote are equivalent
assert(" ' " == ' \' ' and ' " ' == " \" "); // back-slash escape
assert("\n".charAt(0) == 10); // back-slash newline (more)
var x: String; assert(x == ""); // default empty string
assert("" == null); // null is empty
assert("foo" instanceof String);
assert("Number {2+3}." == "Number 5."); // Embedded expression
def a = "foo"; def b = "bar"; assert("{a}{b}" == "foobar");
assert(a + b == "foobar"); // no + operator for Strings
assert("foo {'bar'}" == "foo bar");
assert("{"foo {"bar"}"}" == "foo bar"); // Nested expressions
assert(1s == 1000ms); // 1 second is 1000 milliseconds
assert(1m == 60s); // 1 minute is 60 seconds
assert(1h == 60m); // 1 hour is 60 minutes
assert(3600000ms == 1h); // 1 hour is 3600000 milliseconds
assert(1s instanceof Duration);
assert(0.5m == 30s); // non-integer duration units
assert(0.25h == 900s);
assert(1m + 30s == 90s); // duration arithmetic
assert(1m - 15s == 45 * 1000ms);
assert(1h / 10m == 6);
assert(1h / 10 == 6m);
assert((1h - 50m - 500s - 9500ms)/2 == 45.25s);
assert(10h * 5m); // illegal multiplication
assert(10s + 5); // illegal addition
Sequences
def a = [10, 20, 30, 40, 50]; // sequence of integers
def b = [35, 1.2, "foo", null, false]; // mixed types
def c : String[] = ["a", "b", "c"]; // explicit type
def d : Object[] = [1, "a", null, 2.2, true]; // Object can contain everything
def e : Object[] = null; assert(e == []);
assert([] == null); // null is empty
assert(sizeof a == 5); // sequence length
assert(a == [5+5, 4*5, 60/2, 50-10, 10*5]); // embedded expressions
assert([1, [2, 3, []], [4, 5]] == [1, 2, 3, 4, 5]); // flattening (more)
assert([1..3] == [1, 2, 3]); // range
assert([1..6 step 2] == [1, 3, 5]); // range with step
assert([4..2 step -1] == [4, 3, 2]); // negative step
assert(reverse [1, 5, 10] == [10, 5, 1]); // reverse a sequence
assert(reverse [1..3] == [3..1 step -1]);
def a = [10, 20, 30, 40, 50];
assert(a[1] == 20); // read single element
assert(a[7] == 0); // default value if index does not exist
assert(a[1..3] == [20, 30, 40]); // slicing (inclusive)
assert(a[1..<3] == [20, 30]); // slicing (exclusive)
assert(a[1..] == [20, 30, 40, 50]); // end is optional (inclusive)
assert(a[1..<] == [20, 30, 40]); // end is optional (exclusive)
assert(a == [ a[0..1], a[2..] ]);
var a = ["a", "b", "c", "d"];
a[1] = "BBB"; assert(a == ["a", "BBB", "c", "d"]); // replace member
a[4] = "d"; assert(a == ["a", "BBB", "c", "d"]); // does nothing (index too large)
a[4] = ["d"]; // assigning a sequence not allowed
def b = ["a", "b", "c", "d"];
b[1..1] = ["b1", "b2", "b3"]; // replace slice
assert(b == ["a", "b1", "b2", "b3", "c", "d"]);
def c = ["a", "b", "c", "d"];
c[0..<2] = []; assert(c == ["c", "d"]); // delete slice
c[-1..-1] = ["a", "b"]; assert(c == ["c", "d"]); // does nothing
def d = ["c", "d"];
d[0..0] = ["a", "b", d[0]]; // better use insert statement
assert(d == ["a", "b", "c", "d"]);
def e = ["a", "b", "c", "d"];
e[4..4] = ["e"]; // append (better use insert statement)
assert(e == ["a", "b", "c", "d", "e"]);
def a = [12, -2, 0, 21, -100, 3];
assert(a[x | x >= 0] == [12, 0, 21, 3]); // select clause to filter sequences
assert(a[c | c < -10 or c > 10] == [12, 21, -100]);
assert(a[c | indexof c mod 2 == 0] == [12, 0, -100]); // indexof is the current index
var a = ["a", "b", "c"];
insert "d" into a; assert(a == ["a", "b", "c", "d"]); // appends elements
insert ["e", "f"] into a; assert(a == ["a", "b", "c", "d", "e", "f"]); // append sequence
var b = [1, 2, 3];
insert 34 before b[2]; assert(b == [1, 2, 34, 3]); // insert before
insert [-1, 0] before b[0]; assert(b == [-1, 0, 1, 2, 34, 3]);
var c = [1, 2, 3];
insert 34 after c[2]; assert(c == [1, 2, 3, 34]); // insert after
insert [22, 23] after c[0]; assert(c == [1, 22, 23, 2, 3, 34]);
var d = [1, 2, 3, 4, 5, 6];
delete d[1]; assert(d == [1, 3, 4, 5, 6]); // delete single element
delete d[1..3]; assert(d == [1, 6]); // delete slice
delete d; assert(d == []); // delete all elements
var e = [1, 3, 1, 4, 2, 2, 1];
delete 1 from e; assert(e == [3, 4, 2, 2]); // delete by value
Conditionals and Loops
def a = 5;
if (a > 0) then assert(true);
if (a > 0) assert(true); // then is optional
if (a > 0) assert(true) else assert(false);
var b;
if (a > 0) { // with block (curly braces)
b = 1;
}
else {
b = -1;
}
assert(b == 1);
var c = if (b == 1) "foo" else "bar"; // if/else returns value
def d = if (b == 1) {
println("b is 1.");
"foo"; // last expression in block will be returned
}
else {
println("b is not 1;");
"bar";
};
assert(d == "foo");
var sum = 0;
for (i in [1..4]) // iterates through a sequence
sum += i;
assert(sum == 10);
def a = for (i in [1..3]) i * i; // returns a new sequence
assert(a == [1, 4, 9]);
def b = for (i in [1..7] where i mod 2 != 0) i * i; // restricts sequence
assert(b == [1, 9, 25, 49]);
def c = for (i in [5..7], j in [1..3]) i - j; // two sequences in one loop (more)
assert(c == [4, 3, 2, 5, 4, 3, 6, 5, 4]);
def d = for (i in [1..5] where i mod 2 != 0, j in [1..5] where i <= j) i * j;
assert(d == [1, 2, 3, 4, 5, 9, 12, 15, 25]);
var i = 0;
for (i in [1..5]) // creates new variable named i
sum += i;
assert(i == 0); // i did not change
var a = 0; var b = 3;
while (b > 0) // repeats a block while condition is true
a = a + (b--);
assert(a == 6);
def b = while (a > 0) a--; // while returns Void
var a = ""; var b = ["abc", "d", "efg", "hij", "k", "lmnop", "qrs"];
for (i in b) {
a = "{a}{i}";
if (a.length() >= 10)
break; // aborts the loop
}
assert(a == "abcdefghij");
var c = 0; var d = 0;
while (c <= 10) {
d += c * c;
if (d > 100)
break; // aborts the loop
c++;
}
assert(d == 140);
var a = ""; var b = ["abc", "d", "efg", "hij", "k", "lmnop", "qrs"];
for (i in b) {
if (a.length() >= 10)
continue; // skips the rest of the current iteration
a = "{a}{i}";
}
assert(a == "abcdefghij");
var c = 0; var d = 0;
while (c <= 10) {
c++;
if (d > 100)
continue; // skips the rest of the current iteration
d += c * c;
}
assert(d == 140);
var a = [];
for (i in ["a", "b", "c"])
insert "{i}{indexof i}" into a;
assert(a == ["a0", "b1", "c2"]);
Functions
function isEqual(a, b) { // defining a function
return a == b;
}
assert(isEqual("a", "a")); // calling the function
assert(not isEqual("a", "b"));
assert(isEqual(1, 1)); // Can be called with any type
function isEqual2(a, b) {
a == b // return is optional!
}
assert(isEqual2("a", "a"));
function isEqual3(a: Integer, b: Integer) : Boolean {
a == b // explicitly typed function
}
assert(isEqual3(5, 5));
assert(isEqual3("a", "a")); // incompatible types
def f = function(a) { a * a }; // inline function
assert(f(2) == 4); // called like any other function
function forEach(seq : Integer[], f: function(:Integer)) { // declaration
for (i in seq)
f(i);
}
var sum = 0;
forEach([1, 2, 3], function(a: Integer) : Void { sum += a});
assert(sum == 6);
Classes
class Person { // declaring a class
var lastName : String; // instance variables
var firstName : String = "unknown"; // default value
function toFullName() { // instance function
return "{firstName} {lastName}";
}
}
def a = Person { // creating new Person (more)
firstName: "John" // no comma or semicolon needed
lastName: "Doe"
};
assert(a.firstName == "John"); // property access
assert(a.toFullName() == "John Doe"); // calling a function
a.firstName = "George";
assert(a.firstName == "George");
def b = Person{}; // Omitting properties
assert(b.lastName == ""); // uninitialized default
assert(b.firstName == "unknown");
def c = new Person(); // Java constructor syntax
assert(c.lastName == ""); // uninitialized default
assert(c.firstName == "unknown");
var r: String = "start";
class InitTester {
var a = "vardefault" on replace { r = "{r}-{a}" };
init { r = "{r}-init" } // init block called after variable initialization
postinit { r = "{r}-postinit" } // postinit is called after init
}
var test = InitTester { a: "varinit" };
assert(r == "start-varinit-init-postinit");
import java.lang.Math; // import Java's Math class
class Point {
var x : Integer; var y : Integer;
}
class Shape {
var name : String;
var points : Point[];
}
class ShapeCollection {
var name : String;
var shapes : Shape[];
}
def myShapes = ShapeCollection {
name: "My Shapes"
shapes: [ // sequence as value
Shape {
name: "Triangle"
points: [ Point { x: 0.5 y: 0.5}, Point { x: 1.5 y: 1.5},
Point { x: 0.5 y: 2.5} ]
},
Shape {
name: "Square"
points: [ Point { x: 5 y: 5}, Point { x: 10 y: 5},
Point { x: 10 y: 10}, Point { x: 5 y: 10} ]
},
Shape {
name: "Near-Circle"
points: for (i in [0..<360])
Point { x: 5 + 5 * Math.cos(2.0 * Math.PI * i / 360.0)
y: 15 + 5 * Math.sin(2.0 * Math.PI * i / 360.0) }
}
]
};
class A { var x; var y; }
def a = A { x: 12, y: 12};
def b = A { x: 12, y: 12};
assert(a != b); // Equality not defined! => no 2 objects are equal
assert(a == a); // Same instance => equal
class B {
var x; var y;
override function equals(o: Object) : Boolean {
if (o instanceof B) { // implementing equality
def b2 = o as B;
(x == b2.x) and (y == b2.y)
}
else
false;
}
}
def d = B { x: 12, y: 12};
def e = B { x: 12, y: 12};
def f = B { x: 5, y: 12};
assert(d == e);
assert(d != f);
class Customer extends Person {
var customerNumber : Integer; // adds a new variable
override var firstName = "Bob"; // override to change default
// or provide a different on replace
override function toFullName() { // override to modify behaviour
// calls super implementation:
"{Person.toFullName()} (customer {customerNumber})"
}
}
def myCustomer = Customer {
firstName: "Tom" lastName: "Miller" customerNumber: 3437
};
def a : Person = myCustomer;
assert(a instanceof Person); // checks class of reference
assert(a instanceof Customer); // checks class of reference
assert(a.customerNumber == 3437); // a declared Person, not Customer
assert((a as Customer).customerNumber == 3437); // casting
var r2: String = "start";
class InitTesterA {
var a = "vardefault" on replace { r2 = "{r2}-{a}" };
init { r2 = "{r2}-initA" }
postinit { r2 = "{r2}-postinitA" }
}
class InitTesterB extends InitTesterA {
var b = "vardefault" on replace { r2 = "{r2}-{b}" };
init { r2 = "{r2}-initB" }
postinit { r2 = "{r2}-postinitB" }
}
var test2 = InitTesterB { a: "varinitA", b: "varinitB" };
assert(r2 == "start-varinitA-varinitB-initA-initB-postinitA-postinitB");
mixin class Counter { // defining a mixin
var counter: Integer;
function countUp() { counter++; }
}
mixin class Greeter { // defining a second mixin
function greet(name: String): String { "Hello {name}!" }
}
class GreetingCounter extends Counter, Greeter { // extending both mixins
function countAndGreet(name: String) {
"{greet(name)} You are visitor number {countUp()}."
}
}
def a : GreetingCounter = GreetingCounter{counter: 2};
assert(a.countAndGreet("Tim") == "Hello Tim! You are visitor number 2.");
def b = Counter{counter: 5}; // Mixins can not be instantiated
abstract class A { // abstract class
abstract function foo() : Void; // abstract function
}
class B extends A { // concrete sub-class
override function foo() : Void { println("Hello"); }
}
var a = A{}; // abstract classes can't be created
var b = B{};
Bindings
var a = 5;
var b = bind a; // b has now the value ofa
assert(b == 5);
a = 2;
assert(b == 2); // changes of a appear in b
b = 1; // direct modification not allowed
var c = 3;
b = bind c; // binding only in initialization
def d = bind a; // def and var are equivalent for bind
def e = bind a + c; // binding to an expression (more)
assert(e == 5);
a = 5;
assert(e == 8);
c = 10;
assert(e == 15);
def f = bind { // bind block
def x = if (a > 0) then 10 else -10;
c = 10 // no side-effects allowed
var y = 1; // no variables allowed
x * 10;
};
assert(f == 100);
var v = 2;
var multiplicator = 10;
function mul(x) { // regular function
x * multiplicator
}
bound function mulBound(x) { // bound function
x * multiplicator
}
def a = bind mul(v);
def b = bind mulBound(v);
assert(a == 20 and b == 20);
v = 3; // after argument changes all functions recalculate
assert(a == 30 and b == 30);
multiplicator = 5; // only the bound function changes
assert(a == 30 and b == 15);
v = 3; multiplicator = 5;
def c = bind mul(v) + mulBound(v); // bound function in expression
assert(c == 30);
v = 4;
assert(c == 40);
multiplicator = 10;
assert(c == 60); // only the bound function recalculates
class Point {
var x : Integer; var y : Integer;
}
var a = 5;
def p1 = bind Point{x: a y: 2}; // bind object
assert(p1.x == 5);
def p1Old = p1;
a = 17;
assert(p1.x == 17); // whole object changed
assert(not isSameObject(p1, p1Old)); // new instance created
def p2 = Point{x: bind a y: 2}; // bind property
assert(p2.x == 17);
def p2Old = p2;
a = 25;
assert(p2.x == 25); // property changed
assert(isSameObject(p2, p2Old)); // same old instance
Exceptions (try/catch/finally)
import java.lang.Exception; // JavaFX uses Java's Exception
class MyException extends Exception { // must extend Exception (more)
var msg : String
}
function testException() { // no exception declaration needed
throw MyException{ msg: "got a problem" };
assert(false); // never reached
}
var gotException = false;
try {
testException();
assert(false); // never reached!
}
catch (e : MyException) { // catches only MyException
gotException = true;
assert(e.msg == "got a problem");
}
catch (e : java.lang.RuntimeException) {
assert(false); // never reached
}
assert(gotException);
gotException = false;
try {
throw MyException{ msg: "got another problem" };
assert(false); // never reached!
}
catch (e) { // catch all Throwable
gotException = true;
assert((e as MyException).msg == "got another problem");
}
assert(gotException);
var finallyCounter = 0;
function testFinally(a: Integer) : Boolean {
try {
if (a < 0)
throw new java.lang.Exception("a was negative");
if (a == 0)
return false;
}
finally {
finallyCounter++; // always invoked after leaving try
}
return true;
}
testFinally(1);
testFinally(0);
try { testFinally(-1); } catch (e) { /* ignore exception */ }
assert(finallyCounter == 3);
Java Interoperability
JavaFX has full access to all Java code. You can instantiate, extend and use all Java classes from JavaFX.
import java.util.ArrayList; // import Java class
def list = new ArrayList(3); // create new instance using new
list.add("a"); list.add("b"); list.add("c"); // call methods
var r;
for (i in list) // for can use any Iterable
r = "{r}{i}";
assert(r == "abc");
def t = new java.util.Date(); // without import
def ms = t.time; // getTime is no property
def ms = t.getTime();
def f = new java.io.File("example.dat");
f.<<delete>>(); // escape syntax for keywords (more)
There is currently no direct support for creating and using JavaFX classes from Java. You can start a JavaFX script from Java using the Scripting API though. If you need to call JavaFX code from Java, you should write an interface in Java and implement it in JavaFX.
Packages and Access Modifiers
Packages allow you to structure your JavaFX files (.fx). You can put one or more source files in a directory, and then declare them to be part of a package. The directory name must be the name of the declared package.
By default, all variables, classes and class members can not be accessed from other scripts. You need to use access modifiers like public to publish them to other scripts and packages. The following script animals/bird.fx defines a package and a public class with a public property:
package animals; // declaring package
public class Bird { // public class
public var name : String; // public property
}
To access the Bird class from outside the package, you need to specify the full name:
def myBird : animals.Bird = animals.Bird { name: "Big Bird" };
Alternatively, you must import the name at the beginning of the script and can then use the short name:
import animals.Bird;
def myBird : Bird = Bird { name: "Big Bird" };
If you need several declarations from a package, you can also import them all. However, that makes it more difficult to find out where a declaration is coming from:
import animals.*;
def myBird : Bird = Bird { name: "Big Bird" };
An access modifier can be put in front of a variable, function or class to make them available outside of the current script. By default, only the current script can access them. To give other scripts additional access, the following access modifiers are available:
package var a; // all scripts in the same package have full access
protected var b; // subclasses and scripts in the same package have full access
public var c; // every script has full access
public-read var d; // all scripts can read, only the current script can write
package public-read var e; // all scripts can read,
// only scripts in the same package can write
protected public-read var f; // all scripts can read,
// only the current script can write
public-init var g; // all script can read, all can set it in object initializer,
// only scripts in the same package and subclasses can write
package public-init var h; // all script can read,
// all can set it in object initializer,
// only scripts in the same package can write
protected public-init var i; // all script can read,
// all can set it in object initializer,
// only scripts in the same package and subclasses can write
GUI Features
import javafx.animation.*;
var a = 0.0; var b = 5.0;
def kv1 = a => 100.0; // creates a KeyValue
assert(kv1 instanceof KeyValue);
assert(kv1.interpolate == Interpolator.LINEAR); // default Interpolator
def kv2 = b => 50.0 tween Interpolator.EASEBOTH; // sets Interpolator
assert(kv2 instanceof KeyValue);
assert(kv2.interpolate == Interpolator.EASEBOTH);
import javafx.animation.*;
var x = 0;
def kf1 = at(0s) { x => 0 };
assert(kf1 instanceof KeyFrame);
assert(kf1.time == 0s);
assert(sizeof kf1.values == 1); // contains x => 0
var y = 10; var z = 5;
def kf2 = at(10s) { y => 0; z => 10 }; // KeyFrame with two KeyValues
assert(kf2 instanceof KeyFrame);
assert(kf2.time == 10s);
assert(sizeof kf2.values == 2); // contains [y => 0, z => 10]
JavaFX allows the localization of Strings in the source code. It will automatically look up translations of all localized strings. For this you need to provide a property file with translation for every supported language and every script file. For example, if you store your script in a file called myscript.fx, and you want to provide French (international 2-letter-code 'fr') and German ('de') translations, you also need two files myscript_fr.fxproperties and myscript_de.fxproperties. The format of the file is shown below.
def greetingMorning = ##"Good Morning"; // ## marks string as localized
def greetingEvening = ##[greetEvening]"Good Evening"; // string named 'greetEvening'
Content of myscript_de.properties:
"Good Morning" = "Guten Morgen"
"greetEvening" = "Guten Abend"