JavaFX Script™ (下文中成為JavaFX)語言是一種聲明式的靜態(tài)類型編程語言。它具有第一級函數(shù)(first-class
functions)、聲明式的語法、列表推導(list-comprehensions)及基于依賴關系的增量式求值(incremental
dependency-based evaluation)等特征。JavaFX 腳本式語言特別適用于Java2D swing
GUI組件,它允許簡單地創(chuàng)建圖形界面。
譯者注:第一級函數(shù)指函數(shù)被當作對象對待,可以在運行時賦值、傳遞和返回。詳見wikipedia上的解釋。
譯者注:列表推導指一種在函數(shù)語言中的表達式,它表示了在一個或者多個列表的成員(被選擇的)進行某種操作的結果。它被稱為“syntactic sugar”,即為開發(fā)者提供了便捷的多種函數(shù)的應用組合。詳見FOLDC對list comprehension的解釋。
本文檔給出了JavaFX 腳本式編程語言的非正式描述。
內(nèi)容:
top
JavaFX語言提供四種基本類型:String(字符串)
、Boolean(布爾)
、Number(數(shù)值)
和Integer(整數(shù))
。這些類型相當于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方法傳遞參數(shù)或者從Java方法返回結果的時候,數(shù)值類型會自動執(zhí)行強制類型轉換。并且,在轉換Number
和Integer
的時候還會進行隱式的強制截斷。
top
調(diào)用Java對象
JavaFX可以導入Java類、創(chuàng)建新的Java對象、調(diào)用它們的方法,也可以實現(xiàn)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);
運行上面的程序后,屏幕上顯示如下內(nèi)容:
當然,這并不是JavaFX推薦的創(chuàng)建圖形用戶界面的方式。下面的JavaFX代碼實現(xiàn)了相同的效果:
Frame {
content: Button {
text: "Press Me"
action: operation() {
System.out.println("You pressed me");
}
}
visible: true
}
top
變量
在JavaFX中,var
這個關鍵詞用來聲明變量。你可以在聲明中指定變量的類型。然而,這在JavaFX中是可選的。如果你不指定類型,JavaFX解釋器會根據(jù)它的用途推斷變量的類型。變量聲明使用如下格式:
var variableName : typeName [?,+,*] = initializer;
你可以使
?
、
+
或者
*
操作表示變量的重數(shù)(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];
top
函數(shù)、數(shù)組、表達式和操作
JavaFX函數(shù)
代表了JavaFX編程語言的純函數(shù)子集。函數(shù)體可以僅包含一組變量聲明和一個返回語句。JavaFX也提供了過程(procedures)(被調(diào)用的操作
,詳見下面關于操作的章節(jié)),里面可以包括任意數(shù)量的聲明、條件語句、循環(huán)條件、try/catch等等。語句在函數(shù)中的給定順序并不很重要。下面是一個簡單的函數(shù)程序的例如:
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語言是靜態(tài)類型的,但這里并沒有強制的類型聲明(后面會詳細敘述)。
最常用的數(shù)據(jù)結構是數(shù)組,它在JavaFX中通過方括弧和逗號來聲明:
var week_days = ["Mon","Tue","Wed","Thur","Fri"];
var days = [week_days, ["Sat","Sun"]];
數(shù)組代表了一組順序的對象。JavaFX中的數(shù)組本身不是對象,而且不能嵌套。創(chuàng)建嵌套數(shù)組的表達式(例如上面“days”的初始化方式)會被自動扁平化,例如:
days == ["Mon","Tue","Wed","Thur","Fri","Sat","Sun"]; // returns true
數(shù)組的大小可以通過JavaFX的sizeof
操作符確定:
var n = sizeof days; // n = 7
對于數(shù)組成員形成數(shù)列(arithmetic series)的數(shù)組,JavaFX提供了一種簡寫符號:“
..
”。下面提供了定義階乘函數(shù)和奇數(shù)求和函數(shù)的示例,其中“result”的數(shù)值是1至100中奇數(shù)的和:
function fac(n) {return product([1..n]);}
var result = sum([1,3..100]);
數(shù)組中的所有元素必須是同一種類型。
數(shù)組可以像在Java中那樣通過索引訪問:
var wednesday = days[2];
JavaFX中的
[]
操作符還可用來表示選擇(類似XPath的用法)。在這種情況下,
[]
中包含的表達式應是一個布爾表達式。此表達式可以返回一個新的數(shù)組,此數(shù)組中只包含滿足
[]
中斷言(predicate)的成員。
就像在XPath一樣,可以在[]
操作符包含的斷言中通過.操作符訪問上下文對象。例如:
var nums = [1,2,3,4];
var numsGreaterThanTwo = nums[. > 2]; // yields [3, 4]
另一種方法,也可以將變量聲明為上下文對象。例如,這種方式與上面的方式等價:
numsGreaterThanTwo = nums[n|n > 2];
JavaFX中的indexof
操作符返回對象在數(shù)組中的順序位置(類似XPath中的position()
函數(shù))。
下面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中的空數(shù)組[]
與null
等價,例如:
[] == null // yields true
sizeof null // yields 0
top
修改數(shù)組
除了賦值操作(=
)之外,JavaFX還提供數(shù)據(jù)修改操作符(insert
和delete
),它類似XQuery-Update規(guī)范中的語法和語義:
insert語句
可以用下面方式中的任意一種進行聲明:
insert Expression1 [as first | as last] into Expression2
insert Expression1 before Expression2
insert Expression1 after Expression2
insert
語句將表達式1求值后的返回結果插入到下面表達式中所描述的位置:
into
表達式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]
before, after
表達式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];
top
delete語句
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 []
top
查詢數(shù)組
像在Miranda、Haskell這些函數(shù)式編程語言中一樣,JavaFX支持列表推導(list comprehensions),但是為了使用一種Java程序員更加熟悉、易懂的語法形式進行表達,JavaFX采取了select
和foreach
操作符。
這里提供一個例如:
class Album {
attribute title: String;
attribute artist: String;
attribute tracks: String*;
}
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]
列表推導由一個或多個輸入列表,一個可選的過濾器和一個生成器表達式組成。每個源列表與一個變量關聯(lián)。列表推導的結果是將生成器應用于滿足過濾器的源列表成員的笛卡爾乘積的子集后得到的新列表。
譯者注:這里的過濾器指的是where子句。
列表推導為創(chuàng)建在列表上進行迭代遍歷的通用類提供了一種簡明的語法。
列表推導的另外一個簡單示例:
select n*n from n in [1..100]
這個列表(順序地)包含1至100的所有數(shù)的平方值。注意上面表達式中的“n”是局部變量。
下面的代碼通過定義計算某個數(shù)值的所有因子的函數(shù)演示了如何使用過濾器:
function factors(n) {
return select i from i in [1..n/2] where n % i == 0;
}
top
表達式
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 |
ordinal position |
n/a |
if e1 then e2 else e3 |
conditional expression |
e1 ? e2 : e3 |
select |
list comprehension |
n/a |
foreach |
list comprehension |
n/a |
new |
allocation |
new |
op() |
function/operation call |
n/a |
x.op() |
member function/operation call |
x.op() |
instanceof |
type check |
instanceof |
this |
self access |
this |
. |
attribute access, context access |
., n/a |
bind [lazy] |
incremental [lazy] evaluation |
n/a |
: |
eager initialization |
n/a |
[] |
array selection |
[] |
format as |
String formatting |
n/a |
<<>> |
Identifier quotes |
n/a |
{} |
String expression |
n/a |
(expr) |
grouping |
(expr) |
reverse |
reverses a list |
n/a |
[number1,next..number2] |
numeric range |
n/a |
一些示例:
import java.lang.System;
import java.lang.Math;
var x = 2;
var y = 4;
var a = true;
var b = false;
System.out.println(x == y); // prints false
System.out.println(x <> y); // prints true
System.out.println(x < y); // prints true
System.out.println(x > y); // prints true
System.out.println(x >= y); // prints false
System.out.println(x <= y); // prints true
System.out.println(x + y); // prints 6
System.out.println(x - y); // prints -2
System.out.println(x * y); // prints 8
System.out.println(x / y); // prints 0.5
System.out.println(x % y); // prints 2
System.out.println(a and b); // prints false
System.out.println(a or b); // prints true
System.out.println(not a); // prints false
System.out.println(sizeof [x,y]); // prints 2
System.out.println([x,y][indexof . == 0]); // prints 2
System.out.println(if a then x else y); // prints 2
System.out.println(select q from q in [x, y] where q > 3); prints 4
System.out.println(foreach(q in [x, y] where q < 3) q); prints 2
System.out.println(Math.max(x, y)); // prints 4
System.out.println("abc".toUpperCase()); // prints ABC
System.out.println(x instanceof Number); // prints true
x = 10;
System.out.println(x); // prints 10
top
字符串和字符串表達式
在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不同,在JavaFX中使用雙引號引用的字符串可以包含換行:
var s = "This
contains
new lines";
top
引用標識符
在JavaFX中,包含在法語引用符 <<>>
里的任何字符串序列(包括空白)被作為標識符。這樣允許你使用像class、variable、function或者屬性名這樣的JavaFX關鍵字(或者其它的一般情況下非法的標識符),例如:
var <<while>> = 100;
它也使調(diào)用與JavaFX關鍵字同名的Java方法稱為可能,例如:
import javax.swing.JTextArea;
var textArea = new JTextArea();
textArea.<<insert>>("Hello", 0);
top
范圍表達式
如前所述,通過下面的語法你可以定義一個形成數(shù)列的數(shù)值數(shù)組。
[number1..number2]
此表達式定義了一個從
number1至
number2之間(含兩端)的連續(xù)整數(shù)構成的數(shù)組。
例如:
var nums = [0..3];
System.out.println(nums == [0,1,2,3]); // prints true
默認情況下,值與值的間隔是1
,但是也可以指定不同的間隔,這需要在數(shù)列的number1后面寫出下一個數(shù)值,并用逗號隔開。例如,下面的表達式定義了一個包含1至10之間奇數(shù)的數(shù)組。
[1,3..10]
如果number1比number2要大,會創(chuàng)建降序的數(shù)組:
var nums = [3..0];
System.out.println(nums == [3,2,1,0]); // prints true
top
字符串(String)、數(shù)值(Number)和日期的格式化
JavaFX有內(nèi)建的字符串格式化操作符(format as
),語法如下:
表達式 format as 指令
format as
操作符支持java.text.DecimalFormat
、java.text.SimpleDateFormat
和java.util.Formatter
的格式化指令:如果格式化指令以%開頭,那么將會使用java.util.Formatter
;如果表達式是Number
類型,則使用java.text.DecimalFormat
;如果表達式是java.util.Date
類型,則使用java.text.SimpleDateFormat
。指令操作數(shù)是一個在語法上的標識符,而不是一個表達式。這就允許了在編譯時靜態(tài)檢查指令內(nèi)容的正確性。
例如:
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'
top
操作
在JavaFX中使用operation
關鍵字聲明過程(procedure)。例如:
import java.lang.StringIndexOutOfBoundsException;
operation substring(s:String, n:Number): String {
try {
return s.substring(n);
} catch (e:StringIndexOutOfBoundsException) {
throw "sorry, index out of bounds";
}
}
在上例中定義了一個稱為“substring
”的新過程,它接受兩個參數(shù):第一為字符串類型的參數(shù)“s”,第二為Number類型的參數(shù)“n”,而返回值為字符串類型。
除了從前提到的賦值、delete
、insert
語句之外,下面的語句也可以在過程體中使用:
表達式語句
一個基本的表達式可以被用作一條語句,例如下面示例中的"Hello World!":
System.out.println("Hello World!");
top
If語句
JavaFX的if
語句用法類似Java,除了大括號必須環(huán)繞隨后的子句(除非else子句是另一個if
語句)之外。
例如:
if (condition1) {
System.out.println("Condition 1");
} else if (condition2) {
System.out.println("Condition2");
} else {
System.out.println("not Condition 1 or Condition 2");
}
top
While語句
JavaFX的while
語句用法與Java類似,除了大括號必須環(huán)繞語句體之外。
例如:
var i = 0;
while (i < 10) {
if (i > 5) {
break;
}
System.out.println("i = {i}");
i += 1;
}
top
Try語句
JavaFX的
try
語句用法類似Java,但它具有JavaFX變量聲明語法。
注意:在JavaFX中,任意對象都能夠被拋出和捕捉,而并非僅僅是
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...");
}
top
For語句
JavaFX的
for
語句頭與
foreach
列表推導操作符(list comprehension operator)使用相同的語法。但是,在下面示例中for語句體處理的是由列表推導生成的成員。
例如:
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);
}
top
Return語句
JavaFX的
return
語句與Java相同:
例如:
operation add(x, y) {
return x + y;
}
top
Throw語句
JavaFX的
throw
語句類似Java。但是在JavaFX中能夠拋出任何對象,而不僅僅是
java.lang.Throwable
的繼承類。
例如:
import java.lang.Exception;
operation foo() {
throw new Exception("this is a java exception");
}
operation bar() {
throw "just a string";
}
top
Break和Continue語句
JavaFX的
break
和
continue
語句用法Java不同的之處:前者不支持標簽。像在Java中一樣,
break
和
continue
必須出現(xiàn)在
while
或者
for
語句體中。
例如:
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;
}
}
top
Do語句
JavaFX的do
語句允許使用者在后臺線程中執(zhí)行一塊JavaFX代碼,以便AWT事件調(diào)度線程繼續(xù)處理事件,從而防止UI平臺出現(xiàn)掛起現(xiàn)象。目前,在執(zhí)行后臺線程時采用java.awt.EventQueue
實現(xiàn)了對事件的出/入隊操作。通常情況下,所有的JavaFX代碼都在AWT事件調(diào)度線程中執(zhí)行,只有包含在 do
語句體中的語句被允許在另一個線程中執(zhí)行。這些代碼必須只訪問Java對象,如果需要的話,那些Java對象還必須處理其自身的線程同步。
例如:
import java.net.URL;
import java.lang.StringBuffer;
import java.lang.System;
import java.io.InputStreamReader;
import java.io.BufferedReader;
// in the AWT 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}");
在上面的示例中,在事件調(diào)度線程(EDT)中正在執(zhí)行的那些綠色代碼在do
語句(紅色代碼)執(zhí)行期間將被阻塞。但如果在等待后臺線程完成的期間,一個新的事件調(diào)度循環(huán)被建立在調(diào)用堆棧上,那么在執(zhí)行do
語句的同時這些GUI事件將繼續(xù)被處理。不幸的是,由于它能夠引發(fā)建立在堆棧上的多重事件調(diào)度循環(huán),乃至在糟糕的情況下引起堆棧溢出異常,而目前并沒有一種對此稱得上優(yōu)秀的解決方案。
top
do later
do
語句的第二種形式(do later
):它允許在事件調(diào)度線程中的語句體內(nèi)進行異步執(zhí)行,而不是在后臺線程中執(zhí)行(此功能由java.awt.EventQueue.invokeLater
提供)。顧名思義,do later語句體在事件調(diào)度線程執(zhí)行完成后才被執(zhí)行。下面是一個例如:
import java.lang.System;
var saying1 = "Hello World!";
var saying2 = "Goodbye Cruel World!";
do later {
System.out.println(saying1);
}
System.out.println(saying2);
運行上面的代碼將產(chǎn)生如下輸出:
Goodbye Cruel World!
Hello World!
top
類與對象
JavaFX中聲明類的語法:在class
關鍵字后面跟著類名,接著是可選的extends
關鍵字和由逗號分割的基類名列表,一個開放的大括號({),一個屬性列表,函數(shù)和操作,一個關閉的大括號(}),在大括號中間的每一個語法邏輯行都使用分號結尾。
例如:
class Person {
attribute name: String;
attribute parent: Person inverse Person.children;
attribute children: Person* inverse Person.parent;
function getFamilyIncome(): Number;
function getNumberOfChildren(): Number;
operation marry(spouse: Person);
}
屬性的聲明方式:attribute
關鍵字后面跟著屬性名,一個冒號,屬性類型,可選的重數(shù)(cardinality)說明(?
代表不確定,*
代表零個或者更多,+
代表一個或者更多),一個可選的、用來說明與類中另一屬性之間雙向關系的反向子句(inverse
clause),并使用分號結束。
attribute AttributeName : AttributeType Cardinality inverse ClassName.InverseAttributeName;
如果反向子句出現(xiàn)在對象屬性定義中,那么當此屬性值被修改時JavaFX解釋器將自動更新其反向屬性(根據(jù)更新的類型和屬性的重數(shù)進行插入、刪除或者替換)。
多值屬性(例如那些使用*
或者+
重數(shù)描述符聲明的屬性)被表示為數(shù)組,并能夠通過[]
操作符訪問,并使用insert
和delete
操作符更新。
和Java方法不同,所有的JavaFX成員操作體和成員函數(shù)體都被定義在類聲明外部,例如:
function Person.getNumberOfChildren() {
return sizeof this.children;
}
在類聲明中對操作和函數(shù)的聲明都需要對參數(shù)和返回值類型進行聲明,而在操作體和函數(shù)體的具體定義中則可被忽略。
top
屬性聲明
在JavaFX中可以聲明屬性的初始值。在新建對象上下文中的初始化程序按照聲明順序被逐一求值(見下例的粗體部分):
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
還可以通過bind
操作將增量式求值表達式(incrementally evaluated expression)聲明為屬性值:
import java.lang.System;
class X {
attribute a: Number;
attribute b: Number;
attribute c: Number;
}
attribute X.a = 10;
attribute X.b = bind a + 10;
attribute X.c = bind lazy b + 10;
var x = new X();
System.out.println(x.a); // prints 10
System.out.println(x.b); // prints 20
System.out.println(x.c); // prints 30
x.a = 5;
System.out.println(x.a); // prints 5
System.out.println(x.b); // prints 15
System.out.println(x.c); // prints 25
top
對象聲明
JavaFX使用由類名、用大括號包含的屬性初始化程序列表構成的說明性語法來完成對象的初始化。每個初始化程序由屬性名、冒號、定義屬性值的表達式(JavaFX也支持在上下文中進行增量式求值,詳見下面的章節(jié))構成:
var chris = Person {
name: "Chris"
children:
[Person {
name: "Dee"
},
Person {
name: "Candice"
}]
};
JavaFX也支持Java對象的初始化語法。你可以象在Java中一樣傳遞參數(shù)給類構造器:
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
JavaFX允許在對象中聲明本地變量。這些變量只在對象本身的范圍內(nèi)可見。另外,一個引用被初始化對象的變量可以通過var
關鍵字聲明為假屬性(pseudo-attribute),就如下例中的child1和child2一樣:
var chris = Person {
var: me
name: "Chris"
var child1 = Person {
name: "Dee"
parent: me
}
var child2 = Person { name: "Candice" }
children: [child1, child2]
};
top
更新觸發(fā)器
JavaFX類沒有構造器,其屬性也沒有“setter”。作為替代物,JavaFX提供了類似SQL的觸發(fā)器(trigger
)來為使用者提供處理數(shù)據(jù)修改事件的能力。
觸發(fā)器使用trigger
關鍵字聲明。
觸發(fā)器由頭部和代碼體構成。頭部說明了觸發(fā)器應用的事件類型。代碼體則是在特定事件發(fā)生時執(zhí)行的過程。在代碼體中你可以使用任何在操作體中有效的語句。與成員函數(shù)/操作類似,在觸發(fā)器中在代碼體內(nèi)的上下文對象可以通過this
關鍵字訪問。
top
創(chuàng)建觸發(fā)器
你可以在一個新建對象的上下文中聲明一個創(chuàng)建觸發(fā)器:
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
上面的示例中定義了一個在X
類的實例被創(chuàng)建時執(zhí)行的觸發(fā)器。此觸發(fā)器完成了對nums
屬性的初始賦值。
top
插入觸發(fā)器
當一個成員被插入到多值屬性時,我們可以定義一個插入觸發(fā)器:
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
以上示例代碼中,“num”是引用被插入成員的變量(你可以按照自己喜好命名它)。此變量的上下文索引(由indexof
操作符返回)與插入點一致。
top
刪除觸發(fā)器
當一個成員從多值屬性中被刪除時,我們可以定義一個刪除觸發(fā)器:
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
以上示例代碼中,“num”是引用被刪除成員的變量(你可以按照自己喜好命名它)。此變量的上下文索引(由indexof
操作符返回)與刪除點一致。
top
替換觸發(fā)器
當一個單值的屬性值或者多值屬性的成員被替換時,我們可以定義一個替換觸發(fā)器:
import java.lang.System;
class X {
attribute nums: Number*;
attribute num: Number?;
}
trigger on X.nums[oldValue] = newValue {
System.out.println("just replaced {oldValue} with {newValue} at position {indexof newValue} in X.nums");
}
trigger on X.num[oldValue] = newValue {
System.out.println("X.num: just replaced {oldValue} with {newValue}");
}
var x = X {
nums: [12, 13]
num: 100
};
x.nums[1] = 5; // prints just replaced 13 with 5 at position 1 in X.nums
x.num = 3; // prints X.num: just replaced 100 with 3
x.num = null; // prints X.num: just replaced 3 with null
以上示例代碼中,“oldValue”和“newValue”是兩個變量,它們分別表示對被替換成員的舊值和替換后的新值的引用(你可以按照自己喜好命名)。變量的上下文索引(由indexof
操作符返回)與被替換成員的順序位置一致。
top
在JavaFX中,屬性初始化程序能夠使用bind操作符進行懶惰、增量式求值。使用bind
初始化的屬性類似于包含公式的電子表格中的單元格。在包含此屬性的對象的生命周期中,只要在初始化表達式右側引用的任何對象發(fā)生改變,其左側的對象屬性將被自動更新。示例如下:
import java.lang.System;
class X {
attribute a: Number;
attribute b: Number;
attribute c: Number;
}
trigger on X.b = newValue {
System.out.println("X.b is now {newValue}");
}
trigger on X.c = newValue {
System.out.println("X.c is now {newValue}");
}
var x1 = X {
a: 1
b: 2 // X.b is now 2 is printed
c: 3 // X.c is now 3 is printed
};
var x2 = X {
a: x1.a // eager, non-incremental
b: bind x1.b // eager, incremental (X.b is now 2 is printed)
c: bind lazy x1.c // lazy, incremental (nothing is printed yet)
};
System.out.println(x2.a); // prints 1
System.out.println(x2.b); // prints 2
System.out.println(x2.c); // prints X.c is now 3, then prints 3
x1.a = 5;
x1.b = 5; // prints X.b is now 5, twice
x1.c = 5; // prints X.c is now 5, twice
System.out.println(x2.a); // prints 1
System.out.println(x2.b); // prints 5
System.out.println(x2.c); // prints 5
上例中,x2的屬性b和c被綁定到x1的屬性b和c。這意味著當x1的b或c屬性被更新時,x2的b或c屬性都會相應地被更新。在x2中的b、c屬性之間的不同是:前者的屬性值在其屬性初始化程序中被立即更新,而后者的綁定直到其值被訪問時才被求值。
注意:函數(shù)體無需bind
操作符便可被增量地求值,但操作體則做不到。在改變本地變量的操作中并不觸發(fā)增量式求值。增量式求值不能在操作體內(nèi)執(zhí)行,除了表達式明確地以bind
作為前綴。
然而,當你在一個增量式求值上下文中調(diào)用操作或者Java方法,此調(diào)用本身將被增量式求值。這意味著如果此調(diào)用變成了對操作或者Java方法的全新調(diào)用(由于此調(diào)用被增量式求值),那么它所用到的任何參數(shù)值將被使用并返回新值。
譯者注:此處的新值是與不進行增量式求值相比。
相反,在增量式求值上下文中調(diào)用函數(shù),此函數(shù)只能被調(diào)用一次,而其求值結果也將被合并到調(diào)用求值樹中。
增量式求值是JavaFX的主要特征,它使定義復雜的動態(tài)GUI聲明成為了可能。懶惰求值的特性常用在處理像tree或者graph這樣的遞歸數(shù)據(jù)結構上。
top
反射
JavaFX類、屬性、操作可以通過如下方式反射:
public class Class {
public attribute Name: String;
public attribute Documentation:String?;
public attribute Superclasses: Class* inverse Class.Subclasses;
public attribute Subclasses: Class* inverse Class.Superclasses;
public attribute Attributes: Attribute* inverse Attribute.Scope;
public attribute Operations: Operation* inverse Operation.Target;
public function instantiate();
}
public class Operation extends Class {
public attribute Target: Class? inverse Class.Operations;
}
public class Attribute {
public attribute Name: String;
public attribute Documentation: String?;
public attribute Scope: Class? inverse Class.Attributes;
public attribute Type: Class?;
public attribute Inverse: Attribute* inverse Attribute.Inverse;
public attribute OneToOne: Boolean;
public attribute ManyToOne: Boolean;
public attribute OneToMany: Boolean;
public attribute ManyToMany: Boolean;
public attribute Optional: Boolean;
}
JavaFX支持通過class
操作符對類、屬性、成員函數(shù)和操作的進行反射訪問:
import java.lang.System;
System.out.println(1.class.Name) // prints "Number"
System.out.println("Hello".class.Name); // prints "String"
class X {
attribute a: Number;
}
var x = new X();
System.out.println(x.class.Name); // prints "X"
System.out.println(sizeof x.class.Attributes); // prints 1
System.out.println(x.class.Attributes[0].Name); // prints "a"
對屬性值進行反射訪問時,如果訪問操作數(shù)是屬性,則使用[]
操作符:
import java.lang.System;
class X {
attribute a: Number;
}
var x = new X();
x.a = 2;
System.out.println(x[x.class.Attributes[Name == 'a']]); // prints 2
// the above statement is equivalent to this non-reflective code:
System.out.println(x.a);
在JavaFX中,類的成員函數(shù)和操作本身被模式化作為在目標類中的類,而形參和返回值被表示為屬性。代表目標對象的屬性名是“this”。代表返回值的屬性名為“return”。代表形參的屬性具有和形參相同的屬性名。
譯者注:這里的目標類是指使用成員函數(shù)和操作的類。而目標對象則指使用成員函數(shù)和操作的對象。
從上例中可以發(fā)現(xiàn),你也可以從Class
對象中獲取相同的、被反射的操作。
被反射的操作能夠像函數(shù)那樣通過將目標對象作為第一個參數(shù)、其它參數(shù)作為后面的參數(shù)的方式被調(diào)用:
import java.lang.System;
class X {
operation foo(n: Number): Number;
}
var x = new X();
var op = x.class.Operations[Name == 'foo'];
System.out.println(op(x, 100));
// the above code is equivalent to the following non-reflective code:
System.out.println(x.foo(100));
bean屬性和Java類的公共字段被反射為JavaFX屬性。但是,Java方法不能被反射為JavaFX操作。如果你想調(diào)用某個Java方法,那么你可以通過簡單地使用Java API來實現(xiàn)。
注意:與Java不同的:在JavaFX中class
操作符被用于表達式,而不是用于類型名(type name)。因此作為補充,JavaFX支持從類型名中獲取反射類對象的語法:
:TypeName
For example:
import java.lang.System;
System.out.println(:System.Name); // prints "java.lang.System"
System.out.println(:System.class.Name); // prints "Class"
top
廣度(Extents)和枚舉
類的“廣度”,即此類的所有實例的集合,能夠通過以下語法獲得:
*:ClassName
例如,下面的代碼打印出String
類的所有實例:
import java.lang.System;
for (i in *:String) {
System.out.println(i);
}
注意:這是可選特性,默認情況下是失效的。
JavaFX也提供了聲明類的命名實例的能力:
objectName:ClassName
例如:
import java.lang.System;
myString:String = "This is a string";
System.out.println(myString:String);
這樣的命名實例是全局可訪問的,但通常必須使用類名進行限制。然而,在屬性初始化程序和賦值的上下文中,表達式類型的命名實例被引入到了詞法作用域
(lexical scope)(可見性弱于變量和屬性),并可以通過使用它們的無限定名(unqualified names)引用這些命名實例:
Button {
mnemonic: P
text: "Press Me"
}
在上面實例中,由于Button
的mnemonic
屬性是KeyStroke
類型的,因此我能夠通過使用它的無限定名訪問其命名值P
,而在別處我將不得不使用P:KeyStroke
來引用它。
JavaFX使用與Java1.5同樣的語法來訪問枚舉類型值:
import java.lang.management.MemoryType;
var heap = HEAP:MemoryType;
var nonHeap = NON_HEAP:MemoryType;
top
相關資源
top
凡是有該標志的文章,都是該blog博主Caoer(草兒)原創(chuàng),凡是索引、收藏
、轉載請注明來處和原文作者。非常感謝。