3.1 把Scala当作简洁的Java
Scala拥有非常高的代码密度——键入更少却获得更多。我们从一段Java代码的例子开始:
ScalaForTheJavaEyes/Greetings.java
//Java code
public class Greetings {
public static void main(String[] args) {
for(int i = 1;i< 4; i++) {
System.out.print(i + ",");
}
System.out.println("Scala Rocks!!!");
}
}
输出如下:
1,2,3,Scala Rocks!!!
Scala使上面的代码变得更简洁。首先,它并不关心是否使用分号。其次,在如此简单的例子里,把代码放到Greetings
类里并没有什么真正的益处,因此,可以去掉。再次,没有必要指定变量i
的类型。Scala很聪明,足以推演出i
是一个整数。最后,Scala使用println
输出字符串,不必键入System.out.println
。把上面的代码简化成Scala,如下:
ScalaForTheJavaEyes/Greetings.scala
for (i <- 1 to 3) {
print(i + ",")
}
println("Scala Rocks!!!")
运行上面的Scala脚本,键入scala Greetings.scala
,或是在IDE里运行。
你应该看到这样的输出:
1,2,3,Scala Rocks!!!
Scala的循环结构相当轻量级。只要说明索引i
的值是从1
到3
即可。箭头(<-
)左边定义了一个val
,而不是var
(参见下面的注解),右边是一个生成器表达式(generator expression)。每次循环都会创建一个新的val
,用产生出来的连续值进行初始化。
val vs. var
不管是val还是var,都可以用来定义变量。用val定义的变量是不可变的,初始化之后,值就固定下来了。用var定义的变量是可变的,修改多少次都行。
这里的不变性指的是变量本身,而不是变量所引用的实例。比如说,如果写
val buffer = new StringBuffer()
,就不能把buffer
指向其他的引用。但我们依然可以用诸如append()
之类的方法来修改StringBuffer
的实例。另外,如果用
val str = "hello"
定义了一个String
的实例,就不能再做修改了,因为String
本身也是不可变的。要想让一个类的实例不可变,可以把它的所有字段都定义为val
,然后只提供读取实例状态的方法,不提供修改的方法。在Scala里,应该尽量优先使用
val
,而不是var
;这可以提升不变性和函数式风格。
上面代码产生的范围包含了下界(1
)和上界(3
)。用until()
方法替换to()
方法,就可以从范围内排除上界。
ScalaForTheJavaEyes/GreetingsExclusiveUpper.scala
for (i <- 1 until 3) {
print(i + ",")
}
println("Scala Rocks!!!")
你会看到如下输出:
1,2,Scala Rocks!!!
是的,你没听错。我确实说to()
是方法了。实际上,to()
和until()
都是RichInt
的方法①,这个类型是由Int
隐式转换而来的,而Int
是变量i
的推演类型。这两个函数返回的是一个Range
的实例。因此,调用1 to 3
等价于1.to(3)
,但前者更优雅。在下面的注解中,我们会更多的讨论这一迷人的特性。
①我们会在3.2节“Java基本类型对应的Scala类”中讨论富封装器(rich wrapper)。
点和括号是可选的
如果方法有
0
或1
个参数,点和括号是可以丢掉的。如果方法的参数多于一个,就必须使用括号,但是点仍然是可选的。你已经看到这样做的益处:a+b
实际上是a.+(b)
,1 to 3
实际上是1.to(3)
。利用这样轻量级语法的优势,可以创建出读起来更自然的代码。比如,假设为类
Car
定义了一个turn()
方法:
def turn(direction: String) //…
用轻量级语法调用上面的方法,如下:
car turn "right"
享受可选的点和括号,削减代码中的杂乱吧!
在上面的例子里,看上去是在循环迭代中给i
重新赋了值。然而,i
并不是一个var
,而是一个val
。每次循环都创建一个不同的val
,名字叫做i
。注意,我们不可能由于疏忽在循环里修改了i
的值,因为i
是不变的。这里,我们已经悄然向函数式风格迈进了一步。
使用foreach()
,还可以用更贴近函数式的风格执行循环:
ScalaForTheJavaEyes/GreetingsForEach.scala
(1 to 3).foreach(i => print(i + ","))
println("Scala Rocks!!!")
输出如下:
1,2,3,Scala Rocks!!!
上面的例子很简洁,没有赋值。我们用到了Range
类的foreach()
方法。这个方法以一个函数值作为参数。所以,要在括号里面提供一段代码体,接收一个实参,在这个例子里面命名为i
。=>
将左边的参数列表和右边的实现分离开来。