本文链接: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,可以减少大量的样板代码,使程序变得更加精致。