原文地址:http://java.sun.com/developer/technicalArticles/scripting/javafxpart1/
JavaFX Script编程语言(以下称为JavaFX)有Sun微系统公司开发的一种declarative, statically typed(声明性的、静态类型)脚本语言。如Open JavaFX(OpenJFX)网站所述,JavaFX技术有着良好的前景,包括可以直接调用Java API的能力。因为JavaFX Script是静态类型,它同样具有结构化代码、重用性和封装性,如包、类、继承和单独编译和发布单元,这些特性使得使用Java技术创建和管理大型程序变为可能。
这一系列的JavaFX入门文章包括三部分。第一部分是JavaFX编程语言的介绍,目标读者是那些熟悉Java技术并且具有脚本语言基础的开发者。第二和第三部分演示如何使用JavaFX连接使用Remote Method Invocation(RMI)和Java API for XML Web Services(JAX-WS)技术的远程服务器
JavaFX Pad应用程序
如果在你的系统上已经安装了JRE,最简单的入门方式就是打开Java Web Start激活演示程序——JavaFX Pad。一旦运行这个该程序,应该可以看到类似图1所示的界面:
图1 运行在Windows Vista,JDK6上的JavaFX Pad
JavaFX Pad启动时加载并执行一个默认的程序。不过,你也可以从本文的例子中复制代码,粘贴到源码去,然后观察变化。另外,你也可以将代码保存到本地,并从本地加载JavaFX源文件。JavaFX Pad可以实时的查看在运行时你做了什么,做了改变,立即可以看到结果。
JavaFX技术:一种Statically Typed语言
JavaFX编程语言是一中有着
static typing特性的脚本语言。具体怎么理解呢?来看下面的例子:
var
myVariable
=
"
Hello
";
这种变量的声明方式很类似于JavaScript技术中的变量生命方式,创建一个名称为myVariable的变量,并将其赋值为一个字符串“Hello”。然而,在声明之后我们重新给它赋值:
myVavriable
=
12345
;
因为在12345两端没有引号,所以这个变量的值现在被改为了整数。在JavaScript中,是允许动态改变类型的。而静态类型语言如JavaFX是不允许这样做的。这是因为myVariable已经被初始化为了String类型,而后面的代码却尝试重新赋给它一个整数类型的值。在JavaFX中,一个变量被声明为了String类型,就一直保持String类型。
其实,如果把上面的两行代码输入到JavaFX Pad的demo中,立即就会在窗口的下方看到错误提示,如图2:
图2 JavaFX中静态类型变量不能其改变类型
JavaFX技术:一个Declarative脚本语言
JavaFX技术也是一种declarative脚本语言。这里的
declarative是什么意思呢?为了回答这个问题,来看
OpenJFX网站上的Hello World程序:
class
HelloWorldModel
...
{
attribute saying: String;
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
var model
=
HelloWorldModel
...
{
saying: "Hello World"
}
;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
var win
=
Frame
...
{
title: bind "{model.saying} Java FX"
width:200
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
content: TextField...{
value: bind model.saying
}
visible: true
}
多数编译性语言,包括Java,被认为是命令式编程语言(imperative programming language)。其中,它们依赖于一个程序的入口,如Java中的main()方法。它们通过这个入口点实例化其他类或字段,或设置基于变量或程序状态的资源。为了稍微扩展一下这个例子,你也可以说命令式编程语言,在运行时“公式化”地决定其运行的途径。尽管这个公式可能导致每次运行都是同样的途径,但是这种语言仍旧在运行决定其运行途径。
注意,上面JavaFX的Hello World程序并没有包含main()方法。脚本引擎在执行之前读入整个程序,然后让解释器执行其所需要的任何步骤以正确运行程序。更准确地说,引擎需要的所有东西都要在执行之前声明,然后引擎通过声明决定做什么以达到预期目标。
在JavaFX Pad中使用System.out.prinltn()
接下来我们将要看到的是,JavaFX调用传统Java类库的能力。然而,在你希望在JavaFX Pad程序中使用
System.out.println()之前,需要打开控制台支持,操作如下:
1、Microsoft Windows XP或者Vista,点击控制面板中的Java图标,选择高级选项卡,从Java控制台下的条目中选择“显示控制台”。
2、Solaris操作系统,点击参数(Preferences)选项卡中的Java图标,选择高级选项卡,从Java控制台下的条目中选择“显示控制台”。如果在参数选项卡中没有Java图标,运行
bin目录下的控制面板程序(或
jcontrol)。
3、Linux操作系统,在
bin目录中查找控制面板(或
jcontrol)程序,运行,点击参数选项卡中的Java图标,选择高级选项卡,然后从Java控制台下的条目中选择“显示控制台”。
4、Mac OS X操作系统,打开
/Application/Utilities/Java/[Java version]/下的Preferences程序。选择高级选项卡,然后从Java控制台下的条目中选择“显示控制台”。注意:如果在Java Preferences更改后Java Web Start在Intel Mac上启动错误,尝试打开home目录下的
Library/Caches/Java/deployment.properties文件,然后将所有的
osarch的值由
i386改为
ppc.
在JavaFX Pad中,取消对Run菜单下的Run Automatically的选择,在手动运行JavaFX程序前清空Java控制台。使用JavaFX Pad中的Run菜单下的Run菜单项可以手动运行程序。
图3是JavaFX Pad及控制台运行在Intel-based Macintosh上。图4是JavaFX Pad运行在OpenSolaris上。
图3 运行在Mac OS X上的JavaFX Pad和Java控制台,JDK1.5.0_07
图4 运行在OpenSolaris上的JavaFX Pad及Java控制台,JDK6
最后,如果你正要将变量嵌入到JavaFX的字符串中,这在Java中使用System.out.println()方法时非常普通的操作,注意在JavaFX中的正确语法是:
import java.lang.System;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
System.out.println(
"
Text {variable} and more text
"
);
与Java语法不同:
import
java.lang.System;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
System.out.println(
"
Text
"
+
variable
+
"
and more text
"
);
开始了解JavaFX技术
这一部分讨论JavaFX的基础知识。其中的大部分内容直接来自于官方的
JavaFX编程语言参考文档(JavaFX Programming Language Reference),本文作者针对Java编程人员做了一些修改。
原始类型
JavaFX仅支持四种原始类型:
String,
Boolean,
Number和
Integer。注意,和Java不一样,JavaFX的原始类型首字母大些。表1列出了JavaFX解释器内的原始类型及与之对应的Java对象。
表1:JavaFX的原始类型及其和Java的对应关系
JavaFX原始类型 | 相应的Java原始类型或类 |
String | java.lang.String |
Boolean | java.lang.Boolean |
Number | java.lang.Number |
Integer | byte,short,int,long, java.math.BigInteger |
注意:简便期间,
Integer表示小数字和大数字,而Java中可能会使用不同的原始类型如
short和
long。
浮点数,如Java中的float和double,全部有Number类型替代。
由于Java对象代表了这些原始类型,因此你可以调用Java中已存在的这些类型的方法。
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
如果你想看到这些表达式的结果,就把它们放到System.out.println()语句中,并且确保在顶端导入了java.lang.System。同样,如果得到了一个incompatible type的错误,就在这段脚本的结束位置加入一个返回null的语句。如下:
import java.lang.System;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
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
此外,注意关键字var的使用:尽管在Java中没有使用,但是在JavaFX和其他脚本语言中被用来声明一个新的变量。因为JavaFX是静态类型语言,所以在可以声明变量时指定其类型,或者JavaFX解释器通过变量的使用来推断其类型。例如,下面三种形式在JavaFX中都是合法的:
var
s:String;
var s:String = "A New String"
;
var s = "A New String";
第一和第二种声明正是地为变量赋予String类型,然而第三种就是通过等号(=)右边的初始化推断其为String类型。将其更加形式化,JavaFX的变量声明可以使用下面的方式表示:
var variableName [: typeName] [? | + | *] [= initializer];
问号、加号和星号称为基数操作符。如果使用过表达式语言,对它们一定不陌生。可以使用它们中的一个表示变量的基数(成员的数量),如表2所示。
表2:JavaFX基数操作符
操作符 | 意义 |
? | 可选(可以为空) |
+ | 一个或多个 |
* | 0个或多个 |
下面是一个例子:
var nums:Number* = [5, 9, 13];
这个例子声明了一个名为nums的变量,其值被定义为由0个或多个Number类型的实例组成, 初始化值是三个数字:5,9和13。
typeName、基数操作扶和初始化部分都是可选的,因此下面的声明和前一个例子等价:
var nums = [5, 9, 13];
字面量(Literals)
在JavaFX中,字面量字符串由单引号或者双引号指定:
var s = 'Hello'
;
var s = "Hello";
如作者前面所提,变量甚至整个JavaFX表达式都可以使用花括号({})包括的嵌入形式:
var name = 'Joe'
;
var s = "Hello {name}"; // s = 'Hello Joe'
嵌入表达式可能自己又包含引用的字符串,它又包含嵌入表达式:
var answer = true
;
var s = "The answer is {if answer then "Yes" else "No"}";
// s = 'The answer is Yes'
最后,不像Java,双引号中的String字面量可以包含多行:
var s = "
This
contains
new lines";
数组和列表(Array and List Comprehensions)
在前面你可能就已经注意到基数操作符可以创建数组。JavaFX中的数组由方括号和逗号标示。和Java一样,JavaFX数组中的所有元素必须为同一类型。
var
weekdays
=
[
"
Mon
"
,
"
Tue
"
,
"
Wed
"
,
"
Thur
"
,
"
Fri
"
];
var
days
=
[weekdays, [
"
Sat
"
,
"
Sun
"
]];
数组代表了对象序列。在JavaFX中,arrays are not themselves objects。另外,声明嵌套数组(如前面的例子中第二个变量
days的初始化)的表达式会自动填充。
days
==
[
"
Mon
"
,
"
Tue
"
,
"
Wed
"
,
"
Thur
"
,
"
Fri
"
,
"
Sat
"
,
"
Sun
"
];
//
returns true
注意,如果执行
System.out.println(days),就只会看到数组的第一个元素。使用数组索引可以获得数组元素。同样,如果指定一个并不存在的索引,就会得到ArrayIndexOutOfBoundsException,和Java一样,但是在JavaFX中是0.
可以使用
sizeof操作符获得当前数组的大小:
var
n
=
sizeof days;
//
n = 7
还有一种使用两点(..)的简便形式,其表示数组的元素组成一个算术系列。例如,下面代码创建一个100个元素的数组:
var
oneToAHundred
=
[
1
..
100
];
var
arraySize
=
sizeof oneToAHundred;
//
size == 100
JavaFX同样支持i
nsert和
delete语句对数组进行操作。下面是把值插入(
into)到数组中的例子:
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]
除
into以外,还可以使用
before和
after关键字,如下:
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];
一些表达式方括号中的看起来并不成对,不用担心。它们其实是Xquery-Update(类似XPath)谓词。在这种情况下,方括号中的部分不是引用索引而是索引的值。JavaFX会测试数组中的每一个元素直到表达式结果为true,然后应用
insert。例如,最后一个语句迭代数组中的每一个值,当找到一个元素的值为2时就在该值后面插入数字13,该值是数组的第三个元素。表达式也会在这一点停下来。
delete语句以同样的方式工作。注意,如果方括号中的表达式被省略,将会删除整个数组。
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 []
最后,使用
select和
for each操作符进行一些复杂的数组查询操作。这被认为是
list comprehensions。下面是一个使用
select的简单例子:
var
a:Integer
*
=
select n
*
n from n
in
[
1
..
10
];
//
yields [1,4,9,16,25,36,49,64,81,100]
有一种简单的表示形式,如“循环
[1..10]数组中的每一个数字,将其赋给局部变量
n,然后对每一个
n创建一个元素
n的平方,并将其添加到一个
Integer数组
a中”。
可以如下所示增加一个过滤器:
var
a:Integer
*
=
select n
*
n from n
in
[
1
..
10
] where (n
%
2
==
0
);
//
yields [4,16,36,64,100]
定义做如下改变:“循环[1..10]数组中每一个数字,将其赋给一个局部变量n,但是只有当该数字能被2整除时,换句话说,该数字为偶数时。然后,对于每一个结果n,创建一个新的元素n的平方,并将其添加到一个整形数组a中”。
最后,可以想选择语句中添加多个list:
var
a:Integer
*
=
select n
*
m from n
in
[
1
..
4
], m
in
[
100
,
200
] where (n
%
2
==
0
);
//
yields [200, 400, 400, 800]
循环嵌套循环十分有效地创建了笛卡尔乘积的形式。从获得第一个合法的n值2开始,乘上第一个合法的m值100.然后是同样的2乘上下一个合法的m值200。然后,使用下一个合法的n值4重新开始在m(100和200)上的迭代,得到400和800。这是,选择结束,就得到了最终的数组,被放置在数组a中。
使用foreach操作符可以达到同样的效果:
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)
操作符format as支持几种格式化命令,如表3所示:
表3:JavaFX格式化命令
指令 | 格式化使用的类 |
以%引导的格式化命令 | java.util.Formatter |
表达式是Number | java.text.DecimalFormat |
表达式是java.util.Date | java.text.SimpleDateFormat |
下面是一些例子:
import
java.util.Date;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
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'
注意,此例中法语引用符号(或者双尖括号<< >>)的使用。JavaFX将双尖括号内的标识符包括空格看作普通的字符序列。这就允许在JavaFX的关键字或者其他通常的非法标识符如class,variable,function或者属性的名称。如下例所示:
var
<<
while
>>
=
100
;
这个特性是JavaFX可以调用Java中与其关键字相同名称的方法,如下面的例子:
import javax.swing.JTextArea;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
var
textArea
=
new
JTextArea();
textArea.
<<
insert
>>
(
"
Hello
"
,
0
);
声明类(Declaring Classes)
JavaFX声明的一个类型的语法是:
class关键字后面是类的名称,可选的
extends关键字,使用逗号分隔的基类名称。注意,与Java不同,JavaFX可以继承多个类。再接着是一个开放的花括号({),然后是属性、函数和操作列表,每个都使用分号(;)结尾,最后是一个闭合的花括号(})。下面是一个例子:
class Person
...
{
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
attribute name: String;
attribute parent: Person;
attribute children: Person*;
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
function getFamilyIncome(): Number;
function getNumberOfChildren(): Number;
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
operation marry(spouse: Person): Boolean;
}
属性、函数和操作(Attribute, Functions, andOperations)
我们进一步看一下三种类成员的声明。属性的声明形式
attribute关键字,接着是属性的名称,冒号(:),属性的类型,可选的基数声明,还有一个可选的inverse子句。如果使用了基数,这个属性通常被认为是多值的属性(
multivalued attribute)。
将属性的声明语法更加形式化,表示如下:
[: AttributeType] [
?
|
+
|
*
] [inverse ClassName.InverseAttributeName];
结尾可选的
inverse子句指定了和另外一个变量的双向关系。如果出现了inverse子句,无论何时修改了该属性,JavaFX都会自动更新inverse子句中指定的属性,依据该属性的不同操作分别使用
insert或
delete或
replace。
函数(
Functions)代表JavaFX语言的一个纯粹的功能性子集。换句话说,函数体可能只包括一系列的变量声明和一个
return语句。这使得它们称为最合适的属性访问方法(
getter)和简单的数学运算。下面是函数的一些例子:
function
z(a,b)
...
{
var x = a + b;
var y = a - b;
return sq(x) / sq (y);
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
function
sq(n)
...
{return n * n; }
JavaFX中的操作(
Operations)使用operation关键字声明,操作可能很多语句,例如条件语句、循环语句、try和catch语句等等。这使得它们和Java中类方法很类似。当声明主体,首先给出操作的名称,接着是在圆括号中的输入参数,冒号和返回值类型。下面是一个例子:
operation substring(s:String, n:Number): String
...
{
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
try ...{
return s.substring(n);
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
} catch (e:StringIndexOutOfBoundsException) ...{
throw "sorry, index out of bounds";
}
}
初始化声明的属性(Initializing Declared Attributes)
同函数和进程(procedure)的一样,属性的值的初始化在类定义的外部。初始化表达式按照最近创建的对象的上下文中类声明中指定的属性的顺序求值:
import java.lang.System;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
class X
...
{
attribute a: Number;
attribute b: Number;
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
attribute X.a
=
10
;
attribute X.b
=
-
1
;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
var
x
=
new
X();
System.out.println(x.a);
//
prints 10
System.out.println(x.b);
//
prints -1
JavaFX对象可以使用声明式语法的形式初始化,其组成是类名称和花括号包括的属性初始化列表。每一个初始化表达式由属性名称、冒号和定义属性值的表达式顺序组成。注意,关键字
new被省略了。下面是一个等价的例子:
var
myXClass
=
X
...
{
a: 10
b: -1
}
;
声明式语法在JavaFX中被经常用到。然而,Java对象的分配形式同样支持。在初始化Java类的情况下,可以像在Java中一样为类的构造函数传递参数,或者使用声明式语法:
import java.util.Date;
import java.lang.System;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
var
date1
=
new
Date(
95
,
4
,
23
);
//
call a Java constructor
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
var
date2
=
Date
...
{ // create the same date as an object literal
month: 4
date: 23
year: 95
}
;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
System.out.println(date1
==
date2);
//
prints true
函数和操作定义(Function and Operation Definition)
同Java方法不同,函数和操作的主题在类声明的外面定义,Java程序员刚开始可能会被这种语法感到些许不习惯,但是它是相当容易理解的。JavaFX中,函数或操作的名称由其所属类名称引导。返回列在函数或操作名称的后面,由冒号引导。输入参数包含在圆括号中,由逗号隔开,遵循
name: type的语法形式。下面是一个例子:
operation Person.marry(spouse: Person): Boolean
...
{
// Body of operation
}
参数和返回值在类声明的内部声明操作和函数时是必须的。然而,在外面定义时可以省略。因此,可以简化成这样:
operation Person.marry()
...
{
// Body of operation
}
触发器(Triggers)
和Java不同,JavaFX类没有构造函数,JavaFX属性也没有典型的setter方法。取而代之的是,JavaFX提供了一种类似SQL的触发器来处理数据更改事件。这些触发器使用
trigger关键字。
对象创建触发器(Object Creation Triggers)
在最近创建的对象上下文中指定一个创建触发器触发一个动作:
import java.lang.System;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
class X
...
{
attribute nums: Number*;
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
trigger on
new
X
...
{
insert [3,4] into this.nums;
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
var
x
=
new
X();
System.out.println(x.nums
==
[
3
,
4
]);
//
prints true
此例定义了一个触发器,无论何时创建X类的新的实例时就会执行。此时,它在
nums属性中插入两个数字。注意,
this关键字的使用,它表示触发器上下文中的当前对象。
Insert触发器(Insert Triggers)
也可以在向多值属性插入一个元素时触发一个动作:
import java.lang.System;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
class X
...
{
attribute nums: Number*;
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
trigger on insert num into X.nums
...
{
System.out.println("just inserted {num} into X.nums at position {indexof num}");
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
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 触发器(Delete Triggers)
同样的方式,可以在从多值属性中删除一个元素时触发一个动作:
import java.lang.System;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
class X
...
{
attribute nums: Number*;
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
trigger on
delete
num from X.nums
...
{
System.out.println("just deleted {num} from X.nums at position {indexof num}");
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
var
x
=
X
...
{
nums: [12, 13]
}
;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
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触发器(Replace Trigger)
最后,可以在替换一个属性时触发一个动作。在下面的例子中,oldValue和newValue是任意的变量名,分别表示被替换的元素的以前的值和当前的值。可以随意更改为其他变量名:
import java.lang.System;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
class X
...
{
attribute nums: Number*;
attribute num: Number?;
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
trigger on X.nums[oldValue]
=
newValue
...
{
System.out.println("X.nums: replaced {oldValue} with {newValue} at position {indexof newValue}");
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
trigger on X.num[oldValue]
=
newValue
...
{
System.out.println("X.num: replaced {oldValue} with {newValue}");
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
var
x
=
X
...
{
nums: [12, 13]
num: 100
}
;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
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包含了几种类型的语句,它们和其在Java中的副本极其类似但是并不相同。这一部分将概述它们的不同。
If语句(If Statement)
JavaFX的if语句很类似Java中的if语句,除了花括号是必需的:
if
(condition1)
...
{
System.out.println("Condition 1");
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
}
else
if
(condition2)
...
{
System.out.println("Condition2");
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
}
else
...
{
System.out.println("not Condition 1 or Condition 2");
}
While语句(While Statement)
JavaFX的While语句同样需要花括号:
var
i
=
0
;
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
while
(i
<
10
)
...
{
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
if (i > 5) ...{
break;
}
System.out.println("i = {i}");
i += 1;
}
Try,Catch和Throw语句(Try, Catch, and Throw Statements)
JavaFX的
try和
catch语句和Java中的很像,但是使用的是JavaFX变量声明语法。注意,在JavaFX中,任何一个对象都可以被抛出和捕获,并非只是那些继承了
java.lang.Throwbale的对象。
try
...
{
throw "Hello";
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
}
catch
(s:String)
...
{
System.out.println("caught a String: {s}");
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
}
catch
(any)
...
{
System.out.println("caught something not a String: {any}");
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
}
finally
...
{
System.out.println("finally...");
}
For语句(For Statement)
JavaFX的
for语句的开头使用和前面讨论的
foreach list-comprehension操作符同样的语法。语句的主体在每一个由list comprehension产生的元素上执行。下面是一个简单的例子:
for
(i
in
[
0
..
10
])
...
{
System.out.println("i = {i}");
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
//
print only the even numbers using a filter
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
for
(i
in
[
0
..
10
] where (i
%
2
==
0
) )
...
{
System.out.println("i = {i}");
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
//
print only the odd numbers using a range expression
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
for
(i
in
[
1
,
3
..
10
])
...
{
System.out.println("i = {i}");
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
//
print the cartesian product
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
for
(i
in
[
0
..
10
], j
in
[
0
..
10
])
...
{
System.out.println(i);
System.out.println(j);
}
Return语句(Return Statement)
JavaFX的
return语句与Java中的一样:
operation add(x, y)
...
{
return x + y;
}
Break和Continue语句(Break and Continue Statements)
除了不支持标签外,JavaFX的
break和
continue语句和Java中的一样。同Java一样,
break和
continue必须出现在
while或者for语句的主体内:
operation foo()
...
{
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
for (i in [0..10]) ...{
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
if (i > 5) ...{
break;
}
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
if (i % 2 == 0) ...{
continue;
}
System.out.println(i);
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
operation bar()
...
{
var i = 0;
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
while (i < 10) ...{
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
if (i > 5) ...{
break;
}
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
if (i % 2 == 0) ...{
continue;
}
System.out.println(i);
i += 1;
}
}
Do和Do Later语句(Do and Do Later Statement)
JavaFX的do语句允许执行一个JavaFX代码块。然而,do的主体一般在后台线程中执行。正常情况下,JavaFX代码在AWT Event Dispatch Thread(EDT)中执行,只有do语句主体中代码允许在另外一个线程中执行。参见下面的例子:
import java.net.URL;
import java.lang.StringBuffer;
import java.lang.System;
import java.io.InputStreamReader;
import java.io.BufferedReader;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
//
in the AWT Event Dispatch Thread (EDT)
var
result
=
new
StringBuffer();
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
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;
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
while (true) ...{
line = reader.readLine();
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
if (line == null) ...{
break;
}
result.append(line);
result.append(" ");
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
//
now back in the EDT
System.out.println(
"
result = {result}
"
);
当执行
do语句时在EDT中执行的代码会被阻塞。然而,在等待后台进程完成时,栈内会创建一个新的事件分发起循环。因此,当
do语句执行,图形用户接口(GUI)事件可以继续处理。
do语句还有另外一种形式
do later,这允许在EDT中异步执行其主体,而不是在后台线程中同步执行,类似于
java.awt.EventQueue.invokeLater()提供的功能。下面是一个例子:
import java.lang.System;
var
saying1
=
"
Hello World!
"
;
var
saying2
=
"
Goodbye Cruel World!
"
;
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
do
later
...
{
System.out.println(saying1);
}
System.out.println(saying2);
运行上面的代码,可以得到如下输出:
Goodbye Cruel World
!
Hello World
!
Incremental Evaluation(增量式求值)
增量式求值是JavaFX中众多令人激动的特色中一个:它允许程序员定义复杂的、动态的GUI声明。在JavaFX中,属性初始化可以使用bind操作符进行增量式求值。一旦绑定,这些属性的行为就不是字面量了,而更像电子表格中包含了公式的单元格。只要被应用的右边的初始化表达式中的任何对象做了改变,左边的属性值就自动更新。
下面是一个简单的例子:
import java.lang.System;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
class X
...
{
attribute a: Number;
attribute b: Number;
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
var
x1
=
X
...
{
a: 1
b: 2
}
;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
var
x2
=
X
...
{
a: x1.a // nonincremental
b: bind x1.b // incremental
}
;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
System.out.println(x2.a);
//
prints 1
System.out.println(x2.b);
//
prints 2
x1.a
=
5
;
x1.b
=
5
;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
System.out.println(x2.a);
//
prints 1
System.out.println(x2.b);
//
prints 5
此例中,x2的属性b绑定到了x1的属性b上。这就意味着,只要x1的属性b被更新了,x2的属性b也同时被更新。
注意,函数(function)体一直是没有使用bind操作符的增量式求值的,而操作(operation)体不是。不像函数,操作内部的局部变量改变时不会触发增量式求值。除非在操作体的内部在表达式前面显式地使用了bind操作符,否则操作体不会执行增量式求值。
也可以修改例子使用lazy incremental evaluation。这里的绑定只有在属性在第一次被访问后才会起作用。
import java.lang.System;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
class X
...
{
attribute a: Number;
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
var
x1
=
X
...
{
a: 1
}
;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
var
x2
=
X
...
{
a: bind lazy x1.a
// no value is assigned yet
}
;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
System.out.println(x2.a);
//
Now the value is accessed, so x2.a is set to 1
lazy incremental evaluation经常在递归数据结构中使用,如树和图。
总结(Conclusion)
本文简单介绍了JavaFX平台。在第二和第三部分将讨论使用基于JavaFX技术的GUI处理客户端/服务器端通信的方式。
更多信息(For More Information)
<script type="text/javascript">
</script>