JavaFX Script™ (下文中成为JavaFX)语言是一种声明式的静态类型编程语言。它具有第一级函数(first-class functions)、声明式的语法、列表推导(list-comprehensions)及基于依赖关系的增量式求值(incremental dependency-based evaluation)等特征。JavaFX 脚本式语言特别适用于Java2D swing GUI组件,它允许简单地创建图形界面。
译者注:第一级函数指函数被当作对象对待,可以在运行时赋值、传递和返回。详见wikipedia上的解释。
译者注:列表推导指一种在函数语言中的表达式,它表示了在一个或者多个列表的成员(被选择的)进行某种操作的结果。它被称为“syntactic sugar”,即为开发者提供了便捷的多种函数的应用组合。详见FOLDC对list comprehension的解释。
本文档给出了JavaFX 脚本式编程语言的非正式描述。
JavaFX语言提供四种基本类型:String(字符串)、Boolean(布尔)、Number(数值)和Integer(整数)。这些类型相当于Java的如下类型:
|
|
JavaFX |
Java |
String |
java.lang.String |
Boolean |
java.lang.Boolean |
Number |
java.lang.Number |
Integer |
byte,short,int,long,BigInteger |
例如:
var s = "Hello";
s.toUpperCase(); // yields "HELLO";
s.substring(1); // yields "ello";
var n = 1.5;
n.intValue(); // yields 1
(1.5).intValue(); // yields 1
s.substring(n); // yields "ello"
var b = true;
b instanceof Boolean; // yields true
在向Java方法传递参数或者从Java方法返回结果的时候,数值类型会自动执行强制类型转换。并且,在转换Number和Integer的时候还会进行隐式的强制截断。
JavaFX可以导入Java类、创建新的Java对象、调用它们的方法,也可以实现Java的接口。下面的代码片断提供了一个示例:
import javax.swing.JFrame;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.lang.System;
var frame = new JFrame();
var button = new JButton("Press me");
frame.getContentPane().add(button);
button.addActionListener(new ActionListener() {
operation actionPerformed(event) {
System.out.println("You pressed me");
}
});
frame.pack();
frame.setVisible(true);
运行上面的程序后,屏幕上显示如下内容:
当然,这并不是JavaFX推荐的创建图形用户界面的方式。下面的JavaFX代码实现了相同的效果:
Frame {content: Button {action: operation() {
}
}
visible: true
}
在JavaFX中,var这个关键词用来声明变量。你可以在声明中指定变量的类型。然而,这在JavaFX中是可选的。如果你不指定类型,JavaFX解释器会根据它的用途推断变量的类型。变量声明使用如下格式:
var variableName : typeName [?,+,*] = initializer;
你可以使?、+或者*操作表示变量的重数(cardinality),如下表:
|
|
操作符 |
含义 |
? |
Optional (i.e, may be null) |
+ |
One or more |
* |
Zero or more |
例如:
var nums:Number* = [1,2,3];
上面的示例声明了一个新的、名为nums的变量,它值由零个或者多个Number类型组成,其初始值为[1,2,3]。
:typeName、[?,+,*]以及=initializer这些声明部分是可选的,所以下面的方式与上面等价:
var nums = [1,2,3];
JavaFX函数代表了JavaFX编程语言的纯函数子集。函数体可以仅包含一组变量声明和一个返回语句。JavaFX也提供了过程(procedures)(被调用的操作,详见下面关于操作的章节),里面可以包括任意数量的声明、条件语句、循环条件、try/catch等等。语句在函数中的给定顺序并不很重要。下面是一个简单的函数程序的例如:
function z(a,b) {
var x = a + b;
var y = a - b;
return sq(x) / sq (y);
}
function sq(n) {return n * n;}
function main() {
return z(5, 10);
}
尽管JavaFX语言是静态类型的,但这里并没有强制的类型声明(后面会详细叙述)。
最常用的数据结构是数组,它在JavaFX中通过方括弧和逗号来声明:
var week_days = ["Mon","Tue","Wed","Thur","Fri"];
var days = [week_days, ["Sat","Sun"]];
数组代表了一组顺序的对象。JavaFX中的数组本身不是对象,而且不能嵌套。创建嵌套数组的表达式(例如上面“days”的初始化方式)会被自动扁平化,例如:
days == ["Mon","Tue","Wed","Thur","Fri","Sat","Sun"]; // returns true
数组的大小可以通过JavaFX的sizeof操作符确定:
var n = sizeof days; // n = 7
对于数组成员形成数列(arithmetic series)的数组,JavaFX提供了一种简写符号:“..”。下面提供了定义阶乘函数和奇数求和函数的示例,其中“result”的数值是1至100中奇数的和:
function fac(n) {return product([1..n]);}
var result = sum([1,3..100]);
数组中的所有元素必须是同一种类型。
数组可以像在Java中那样通过索引访问:
var wednesday = days[2];
JavaFX中的[]操作符还可用来表示选择(类似XPath的用法)。在这种情况下,[]中包含的表达式应是一个布尔表达式。此表达式可以返回一个新的数组,此数组中只包含满足[]中断言(predicate)的成员。
就像在XPath一样,可以在[]操作符包含的断言中通过.操作符访问上下文对象。例如:
var nums = [1,2,3,4];
var numsGreaterThanTwo = nums[. > 2]; // yields [3, 4]
另一种方法,也可以将变量声明为上下文对象。例如,这种方式与上面的方式等价:
numsGreaterThanTwo = nums[n|n > 2];
JavaFX中的indexof操作符返回对象在数组中的顺序位置(类似XPath中的position()函数)。
下面list的car和cdr可以用选择表达式来表示:
function car(list) {return list[indexof . == 0];}
function cdr(list) {return list[indexof . > 0];}
当然,car可以用更简单、高效的方式表示:
function car(list) {return list[0];}
例如:
var list = [1..10];
car(list); // yields 1
cdr(list); // yields [2,3,4,5,6,7,8,9,10]
JavaFX中的空数组[]与null等价,例如:
[] == null // yields true
sizeof null // yields 0
除了赋值操作(=)之外,JavaFX还提供数据修改操作符(insert和delete),它类似XQuery-Update规范中的语法和语义:
可以用下面方式中的任意一种进行声明:
insert Expression1 [as first | as last] into Expression2
insert Expression1 before Expression2
insert Expression1 after Expression2
insert语句将表达式1求值后的返回结果插入到下面表达式中所描述的位置:
表达式2必须指向一个属性或者变量。如果表达式2指向一个单值属性,那么插入的效果等同于赋值操作。
如果指定了as first,那么插入位置就在表达式2所表示的列表的第一个元素的前面。如果指定了as last,那么插入位置就在表达式2所表示的列表的最后一个元素的后面。如果没有明确地指定as first或者as last,则默认为as last。
例如:
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]
表达式2必须是在属性或者变量之上的选择表达式。如果指定了before,那么插入位置就是在被选择的元素之前。如果指定了after,插入位置则在被选择的元素之后。
例如:
var x = [1,2,3];
insert 10 after x[. == 10]; // 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];
delete语句可以使用下面形式中的一种:
delete variable
delete Expression.attribute
delete variable[predicate]
delete Expression.attribute[predicate]
前两种形式将删除变量或者属性中的所有元素,它们等价于将变量或者属性赋值为[]或者null。后两种形式仅删除满足断言的元素。
例如:
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; // yields []
像在Miranda、Haskell这些函数式编程语言中一样,JavaFX支持列表推导(list comprehensions),但是为了使用一种Java程序员更加熟悉、易懂的语法形式进行表达,JavaFX采取了select和foreach操作符。
这里提供一个例如:
class Album {
}
var albums =
[Album { title: "A Hard Day's Night"
artist: "The Beatles"
tracks:
["A Hard Day's Night",
"I Should Have Known Better",
"If I Fell",
"I'm Happy Just To Dance With You", "And I Love Her",
"Tell Me Why",
"Can't Buy Me Love",
"Any Time At All",
"I'll Cry Instead",
"Things We Said Today",
"When I Get Home",
"You Can't Do That"]
},
Album {
title: "Circle Of Love"
artist: "Steve Miller Band"
tracks:
["Heart Like A Wheel",
"Get On Home",
"Baby Wanna Dance",
"Circle Of Love",
"Macho City"]
}];
// Get the track numbers of the albums' title tracks
// using the select operator:
var titleTracks =
select indexof track + 1 from album in albums,
track in album.tracks where track == album.title; // yields [1,4]
// the same expressed using the foreach operator:
titleTracks =
foreach (album in albums,
track in album.tracks
where track == album.title)
indexof track + 1; // also yields [1,4]
列表推导由一个或多个输入列表,一个可选的过滤器和一个生成器表达式组成。每个源列表与一个变量关联。列表推导的结果是将生成器应用于满足过滤器的源列表成员的笛卡尔乘积的子集后得到的新列表。
译者注:这里的过滤器指的是where子句。
列表推导为创建在列表上进行迭代遍历的通用类提供了一种简明的语法。
列表推导的另外一个简单示例:
select n*n from n in [1..100]
这个列表(顺序地)包含1至100的所有数的平方值。注意上面表达式中的“n”是局部变量。
下面的代码通过定义计算某个数值的所有因子的函数演示了如何使用过滤器:
function factors(n) {
return select i from i in [1..n/2] where n % i == 0;
}
JavaFX支持如下操作符:
|
||
操作符 |
含义 |
Java等价物 |
|
|
|
关系操作符 |
|
|
== |
equality |
== |
<> |
inequality |
!= |
< |
less than |
< |
> |
greater than |
> |
<= |
less than or equal |
<= |
>= |
greater than or equal |
>= |
|
|
|
布尔操作符 |
|
|
and |
logical and |
&& |
or |
logical or |
|| |
not |
logical negation |
! |
|
|
|
算术操作符 |
|
|
+ |
addition |
+ |
- |
subtraction; unary negation |
- |
* |
multiplication |
* |
/ |
division |
/ |
% |
remainder |
% |
+= |
add and assign |
+= |
-= |
subtract and assign |
-= |
*= |
multiply and assign |
*= |
/= |
divide and assign |
/= |
%= |
remainder and assign |
%= |
|
|
|
其它操作符 |
|
|
sizeof |
array length |
n/a |
indexof |