本文链接:Scala学习(三)——代码块和流程控制
简介
如果说类和方法构成了程序的框架,那么实际完成的代码就是程序的血肉。Scala中也提供了常见的流程控制结构诸如if,for,while,do ... while等等,但是其特性和Java相比有所不同。在本文中将会一一介绍。
基本代码块
和Java相同,基本代码块是由{}包围的语句序列。和Java不同的地方在于,Scala的基本代码块是有值的,其值为最后一个语句的值(Scala中的语句都是有值的)。比如如下程序:
| 1 2 3 4 5 6 | var b = 0 val a = {   b += 1   1 } println(a) // 输出1 | 
引申一点:Scala中的方法写法其实是基本代码块语句的一种变体,比如按照类似Java的写法:
| 1 2 3 | def a(): Int = {   return 2 } | 
其实return可以省略,由代码块的最后一条语句作为返回值:
| 1 2 3 | def a(): Int = {   2 } | 
当然,大括号和返回值也可以省略:
| 1 | def a() = 2 | 
虽然此时看上去很像一个变量,但确实是个函数,等号之后的语句只有在调用时才会执行。如果函数体很短,使用这样的写法会使程序更加简洁明了。
if语句
if语句的写法和Java中完全相同:
| 1 2 3 4 5 6 | var b = 0 if(b == 1) {   println("Yes") } else {   println("No")  // 执行 } | 
if语句的返回值是正确的选择支中最后一个语句的返回值:
| 1 2 | var b = 0 val a = if(b == 1) "Yes" else "No" // a = "No" | 
所以if语句可以替代Java中的?:表达式,并且相对直观(不过我个人还是很钟爱?:这种简洁的写法,虽然Scala中不支持)。
while语句和do ... while语句
while语句和do ... while语句的写法和和Java中的完全相同:
| 1 2 3 4 5 | var i = 0 while(i < 10) {   println(i)   i += 1 } | 
| 1 2 3 4 5 | var i = 0 do {   println(i)   i += 1 } while(i < 10) | 
这两种语句的返回值为Unit类型,相当于Java中的void类型。
for语句
Scala中的for语句和Java中完全不同,主要用于访问集合。以一个例子来说明:
| 1 2 3 | for(n <- 0 to 10) { // n是循环变量,0 to 10定义了一个集合   println(n) // 输出0 1 2 ... 10 } | 
也可以同时访问多个集合,效果相当于多层循环:
| 1 2 3 | for(n <- 0 to 10; m <- 0 until 3) { // until定义的集合不包含终值   println(n * 3 + m) // 输出0 1 2 ... 31 32 } | 
和以下代码等效:
| 1 2 3 4 | for(n <- 0 to 10)   for(m <- 0 until 3) {     println(n * 3 + m)   } | 
for语句的实现原理是调用了集合类的foreach方法。只要一个类提供了恰当的foreach方法,其对象就可以放在<-右边进行遍历。另外,0 to 10并不是特殊语法,这一点在之后的章节会讲到。
同其他循环语句一样,for语句的返回值为Unit类型。
break,continue和goto
Scala中没有提供这些语句和相应的功能。实际上,Scala并不是那么提倡使用循环,而是使用尾递归的方式进行函数调用,这一点之后再讲。
match语句
match语句是Scala中提供模式匹配的重要语句。其功能类似Java中的switch,但是功能更加强大,语法更加简洁。其基本用法如下:
| 1 2 3 4 5 6 7 | i match {   case 0 => methodA()   case 1 => methodB()   case _ => {     println("default")   } } | 
首先是要匹配的变量,然后是match关键字,然后是用大括号包含的case语句序列,分支条件和代码块之间用=>分隔。
没有default关键字,而是用case _替代。匹配条件时会匹配首个满足条件的case子句。
因为没有break,所以match语句不提供穿透性质。
如果要匹配多个常量,可以用|分隔,就像下面这样:
| 1 2 3 4 5 6 7 | i match {   case 0 | 2 => methodA()   case 1 => methodB()   case _ => {     println("default")   } } | 
case子句除了匹配常量外,还可以匹配变量类型和提供了unapply方法的对象:
| 1 2 3 4 5 6 7 8 9 10 11 | i match {   case 0 => methodA()   case 1 => methodB()   case x: Int => methodC(x)     // 匹配变量类型,变量名可以随便取   case y: Double => methodD(y)  // 匹配变量类型   case p(x) => methodE(x)       // 匹配提供了unapply方法的对象p,p在match语句外定义   case q(x: Int) => methodE(x)  // 匹配提供了unapply方法的对象q,匹配参数类型   case _ => {     println("default")   } } | 
有关unapply机制,将在以后说明。
match子句的返回值是实际执行分支的返回值,类似if语句。
总结
Scala的流程控制和Java不尽相同,尤其是没有break和continue这种常见的语句。这也是Scala在编程思路上与Java不同的地方。如果使用得当,Scala相比Java,可以减少大量的样板代码,使程序变得更加精致。