表达式是可以生成某个结果值的代码段,可以结合使用表达式来生成“更大的”表达式。JavaFX Script
编程语言是表达式语言,这意味着一切(包括循环、条件甚至块)都是表达式。在某些情况下(如
while
表达式),表达式具有
Void
类型,这意味着它们不返回结果值
块表达式由一系列声明或表达式组成,它们括在花括号中并用分号进行分隔。块表达式的值是最后一个表达式
的值。如果块表达式中不包含表达式,则其类型为 Void
。请注意,var
和 def
是表达式。
下面的块表达式对几个数字进行相加并将结果存储在一个名为 total
的变量中:
var nums = [5, 7, 3, 9];
var total = {
var sum = 0;
for (a in nums) { sum += a };
sum;
}
println("Total is {total}.");
|
运行此脚本将生成以下输出:
第一行 (var nums = [5, 7, 3, 9];
) 声明一个整数序列。
第二行声明一个名为 total
的变量,该变量将用来存放这些整数的和。
随后的块表达式由左花括号和右花括号之间的所有内容构成:
{
var sum = 0;
for (a in nums) { sum += a };
sum;
}
|
在该块内部,第一行代码声明一个名为 sum
的变量,该变量将用来存放此序列中各个数字之和。第二行(一个 for
表达式)遍历该序列,将每个数字与 sum
相加。最后一行设置该块表达式的返回值(在本例中为 24)。
使用 if
表达式后,仅当特定条件为真时才执行某些代码块,从而对程序流
进行定向。
例如,以下脚本基于年龄来设置票价。12 岁到 65 岁的人支付正常价 10 美元。老人和儿童支付 5 美元;5
岁以下的儿童免费。
def age = 8;
var ticketPrice;
if (age < 5 ) {
ticketPrice = 0;
} else if (age < 12 or age > 65) {
ticketPrice = 5;
} else {
ticketPrice = 10;
}
println("Age: {age} Ticket Price: {ticketPrice} dollars.");
|
如果将 age
设置为 8,该脚本将生成以下输出:
Age: 8 Ticket Price: 5 dollars.
|
该示例的程序流如下所示:
if (age < 5 ) {
ticketPrice = 0;
} else if (age < 12 or age > 65) {
ticketPrice = 5;
} else {
ticketPrice = 10;
}
|
如果 age
小于 5,则票价将设置为 0。
程序随后将跳过其余条件测试并输出结果。
如果 age
不小于 5,程序将继续执行下一个条件测试(由后跟另一个 if
表达式的 else
关键字来指示):
if (age < 5 ) {
ticketPrice = 0;
} else if (age < 12 or age > 65) {
ticketPrice = 5;
} else {
ticketPrice = 10;
}
|
如果人的年龄在 5 到 12 岁之间或者大于 65 岁,该程序会将票价设置为 5 美元。
如果人的年龄在 12 到 65 岁之间,程序会流至最后一个代码块(用 else
关键字进行标记):
if (age < 5 ) {
ticketPrice = 0;
} else if (age < 12 or age > 65) {
ticketPrice = 5;
} else {
ticketPrice = 10;
}
|
只有当前面的所有条件均不满足时,才会执行此块。它会针对 12 到 65 岁之间的人将票价设置为 10 美元。
注:可以将上面的代码缩减成一个非常简洁的条件表达式:
ticketPrice = if (age < 5) 0 else if (age < 12 or age > 65) 5 else 10;
|
这是一个需要掌握的有用方法,在本教程的后
面部分中还会使用它。
“序列”一课讲授了一种用来声明形成等差数列的数字序列的简化表示法。
从技术上讲,[0..5]
是一个范围表达式。默认情况下,相邻值之间的间
隔为 1,但是您可以使用 step
关键字来指定一个不同的间隔。例如,定义一个由 1 到 10
之间的奇数构成的序列:
var nums = [1..10 step 2];
println(nums);
|
此脚本的输出如下所示:
要创建降序范围,请确保第二个值小于第一个值,并指定一个负的 step 值:
var nums = [10..1 step -1];
println(nums);
|
输出为:
[ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 ]
|
如果您在创建降序范围时没有提供负的 step 值,则会生成一个空序列。
以下代码:
var nums = [10..1 step 1];
println(nums);
|
将生成下面的编译时警告:
range.fx:1: warning: empty sequence range literal, probably not what you meant.
var nums = [10..1 step 1];
^
1 warning
|
如果您完全忽略 step 值,也会生成一个空序列。
另一个与序列有关的表达式是 for 表达式。for
表达式为遍历序列中的各个项提供了一种方便的机制。
以下代码提供了一个示例:
var days = ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"];
for (day in days) {
println(day);
}
|
此脚本的输出如下所示:
Mon
Tue
Wed
Thu
Fri
Sat
Sun
|
让我们将该示例分成几个部分。for
表达式以 "for" 关键字开头:
for (day in days) {
println(day);
}
|
days
变量是要由 for
表达式处理的输入序列的
名称:
for (day in days) {
println(day);
}
|
当 for
表达式遍历该序列时,day
变量用来存放当前项:
for (day in days) {
println(day);
}
|
请注意,不需要在脚本中的其他位置声明 day
变量即可将其用在 for
表达式中。此外,在完成整个循环之后,将无法访问 day
。程序员通常会赋予临时变量(如该变量)非常短的名称(或由一个
字母构成的名称)。
在上例中,未显示 for
返回值;但 for
也是一个返回序列的表达式。以下代码显示了两个使用 for
表达式从另一个序列创建序列的示例:
// Resulting sequence squares the values from the original sequence.
var squares = for (i in [1..10]) i*i;
// Resulting sequence is ["MON", "TUE", "WED", and so on...]
var capitalDays = for (day in days) day.toUpperCase();
|
请注意,toUpperCase
函数由 String
对象提供。您可以通过查阅 API 文档来查看完整的可用函数列表。
另一个循环结构是 while 表达式。与作用于序列中项的 for
表达式不同,while
表达式会一直循环,直到给定的表达式为 false
为止。尽管 while
在语法上是表达式,但是它的类型为 Void
,不返回值。
下面提供了一个示例:
var count = 0;
while (count < 10) {
println("count == {count}");
count++;
}
|
此脚本的输出如下所示:
count == 0
count == 1
count == 2
count == 3
count == 4
count == 5
count == 6
count == 7
count == 8
count == 9
|
第一行声明一个名为 count
的变量并将其初始化为 0:
var count = 0;
while (count < 10) {
println("count == {count}");
count += 1;
}
|
第二行以 while
表达式开头。此表达式创建了一个循环(在左花括号和右花括号之间),该循环会一直进行,直到 count < 10
的值为 false
为止:
var count = 0;
while (count < 10) {
println("count == {count}");
count += 1;
}
|
While 表达式的主体会输出 count
的当前值,然后将 count
的值加 1:
var count = 0;
while (count < 10) {
println("count == {count}");
count += 1;
}
|
当 count
等于 10 时,循环退出。要创建一个无限循环,请将 true
关键字放在左小括号和右小括号之间,如 while(true){}
中所示。
break
和 continue
表达式与循环表达式有关。这两个表达式会影响循环迭代:break
完全放弃循环,而 continue
仅放弃当前迭代。
尽管 break
和 continue
在语法上是表达式,但它们的类型为 Void
,不返回值。
示例:
for (i in [0..10]) {
if (i > 5) {
break;
}
if (i mod 2 == 0) {
continue;
}
println(i);
}
|
输出:
如果没有 if
表达式,该程序将只是输出 0 到 10 之间的数字。
如果只有第一个 if
表达式,程序将在 i
的值大于 5 时中
断循环:
因此,程序将仅输出 1 到 5 之间的数字。
通过添加第二个 if
表达式,程序将仅放弃循环的当前迭代而继续执行下一
个迭代:
if (i mod 2 == 0) {
continue;
}
|
在这种情况下,只有当 i
为偶数(即 i
能被 2
整除,没有余数)时才执行 continue
。出现这种情况时,将永远不会调用 println()
,
因此输出中将不包含该数字。
在实际的应用程序中,正常的脚本执行流有时会被某个事件中止。例如,如果脚本从某个文件中读取输入,但是找不到该文件,该
脚本将无法继续。我们将这种情况称为“异常”。
注意:异常是对象。它们的类型通常以它们所表示的情况命名(例如,FileNotFoundException
表示找不到文件的情况)。但是,定义一组特定于即将给出的示例的异常不在本节的讨论范围之内。因此,我们将使用一个通用的 Exception
对象(从 Java 编程语言借用而来)来说明 throw
、try
、catch
和 finally
表达式。
以下脚本定义(和调用)一个会抛出异常的函数:
import java.lang.Exception;
foo();
println("The script is now executing as expected... ");
function foo() {
var somethingWeird = false;
if(somethingWeird){
throw new Exception("Something weird just happened!");
} else {
println("We made it through the function.");
}
}
|
按原样运行此脚本(将 somethingWeird
设置为 false
)
将输出以下消息:
We made it through the function.
The script is now executing as expected...
|
但是,如果将该变量更改为 true
,则会抛出异常。在运行时,该脚本将崩溃并显示以下消息:
Exception in thread "main" java.lang.Exception: Something weird just happened!
at exceptions.foo(exceptions.fx:10)
at exceptions.javafx$run$(exceptions.fx:3)
|
为了防止崩溃,我们将需要使用 try/catch 表达式来包装 foo()
调用。顾名思义,这些表达式尝试执行某些代码,但会在出现问题时捕捉异常:
try {
foo();
} catch (e: Exception) {
println("{e.getMessage()} (but we caught it)");
}
|
现在,程序不会崩溃,而只是输出:
Something weird just happened! (but we caught it)
The script is now executing as expected...
|
还有一个 finally 块(它在技术上不是表达式),无论是否抛出了异常,该块始终在 try
表达式退出之后的某个时间执行。finally
块用来执行无论 try
主体是成功还是抛出异常都需要执行的清除操作。
try {
foo();
} catch (e: Exception) {
println("{e.getMessage()} (but we caught it)");
} finally {
println("We are now in the finally expression...");
}
|
程序输出现在为:
Something weird just happened! (but we caught it)
We are now in the finally expression...
The script is now executing as expected...
天苍苍,野茫茫,风吹草底见牛羊