现在你已经看过了Scala的内建控制结构,我们将在本节中使用它们来解释Scala里的范围是如何起作用的。
51CTO编辑推荐:Scala编程语言专题
Scala程序里的变量定义有一个能够使用的范围:scope。范围设定的最普通不过的例子就是,大括号通常引入了一个新的范围,所以任何定义在打括号里的东西在括号之后就脱离了范围。这条规则有几个例外,因为在Scala里有时候你可以用大括号代替小括号。表达式语法的替代品是这种使用大括号例子的其中之一。作为演示,请看一下代码7.18里展示的函数:
代码 7.18 打印乘法表时的变量范围
- def printMultiTable() {
- var i = 1
- // 这里只有i在范围内
- while (i <= 10) {
- var j = 1
- // 这里i和j在范围内
- while (j <= 10) {
- val prod = (i * j).toString
- // 这里i,j和prod在范围内
- var k = prod.length
- // 这里i,j,prod和k在范围内
- while (k < 4) {
- print(" ")
- k += 1
- }
- print(prod)
- j += 1
- }
- // i和j仍在范围内;prod和k脱离范围
- println()
- i += 1
- }
- // i仍在范围内;j,prod和k脱离范围
- }
printMultiTable函数打印了乘法表。 函数的第一个语句引入了变量i并初始化为整数1。然后你可以在函数余下的部分里使用名称i。
printMultiTable接下去的语句是一个while循环:
你可以在这使用i因为它仍在范围内。在while循环的第一个语句里,你引入了另一个变量,叫做j,并再次初始化为1。因为变量j定义在while循环的大括号内,所以只能用在while循环里。如果你想尝试在while循环的大括号之后,在那个说j,prod和k已经出了范围的注释后面,再用j做点儿什么事,你的程序就编译不过了。
- while (i <= 10) {
- var j = 1
- ...
- }
本例中定义的所有变量——i,j,prod和k——都是本地变量:local variable。对于它们被定义的函数来说是“本地”的。每次函数被调用的时候,一整套全新的本地变量将被使用。
一旦变量被定义了,你就不可以在同一个范围内定义同样的名字。比如,下面的脚本不会被编译通过:
然而,你可以在一个内部范围内定义与外部范围里名称相同的变量。下列脚本将编译通过并可以运行:
- val a = 1
- val a = 2 // 编译不过
- println(a)
执行时,这个脚本会先打印2,然后打印1,因为定义在内部打括号里的a是不同的变量,将仅在大括号内部有效。另外,本例中在a的第一个定义之后需要加分号,因为Scala的分号推断机制不会在这里加上分号。Scala和Java间要注意的一个不同是,与Scala不同,Java不允许你在内部范围内创建与外部范围变量同名的变量。在Scala程序里,内部变量被说成是遮蔽:shadow了同名的外部变量,因为在内部范围内外部变量变得不可见了。
- val a = 1;
- {
- val a = 2 // 编译通过
- println(a)
- }
- println(a)
或许你已经注意到了一些在解释器里看上去像是遮蔽的东西:
解释器里,你可以对你的核心内容重用变量名。撇开别的不说,这样能允许你当发现你在解释器里第一次定义变量时犯了错误的时候改变主意。你能这么做的理由是因为,在理论上,解释器在每次你输入新的语句时都创建了一个新的嵌套范围。因此,你可以把之前解释的代码虚拟化认为是:
- scala> val a = 1
- a: Int = 1
- scala> val a = 2
- a: Int = 2
- scala> println(a)
- 2
- val a = 1;
- {
- var a = 2;
- {
- println(a)
- }
- }
这段代码可以像Scala脚本那样编译和执行,而且像输入到解释器里的代码那样,打印输出2。请记住这样的代码对读者来说是很混乱的,因为在嵌套范围中变量名称拥有了新的涵义。通常更好的办法是选择一个新的有意义的变量名而不是遮蔽外部变量。
【相关阅读】