Scala中大多数控制结构都是表达式,有返回值
Scala 只有为数不多的几个内建的控制结构:if、match、for、while、try和函数调用,由于它们有返回值,可以很好地支持函数式编程。
条件控制结构
if表达式
语法:
if (<Boolean expression>) <expression>
:返回值是 Any 类型;if (<Boolean expression>) <expression> else <expression>
:返回值的类型是两种结果类型的最近公共父类型;if (<Boolean expression>) <expression> else if (<Boolean expression>) ... else <expression>
:本质上是if ... else
表达式的嵌套,返回值的类型是所有可能返回结果类型的最近公共父类型;
执行:如果布尔表达式成立则执行第一个表达式,否则执行另外一个表达式
示例:
1 | // if ... 返回值类型必为 Any |
match表达式
模式匹配是检查某个值(value)是否匹配某一个模式的机制,它是Java中的switch语句的升级版,同样可以用于替代一系列的 if/else 语句。
语法:
1 | <expression> match { |
执行:获取输入表达式的值,逐一匹配备选模式,匹配成功则执行并返回对应模式后的表达式,匹配不成功则触发MatchError,返回值类型是各个备选结果表达式类型的最近公共父类型。
示例:
1 | // 对 if (x > y) x else y 的改写 |
变形:match 表达式的变形主要发生在
- 复合模式:使用
<pattern1> | <pattern2> ...
可以对多个模式重用 case 块
1 | scala> "MON" match { |
- 通配模式:使用通配符
_
可以匹配任意模式,但是不能在 => 右侧访问通配符
1 | scala> "MON" match { |
- 变量模式:使用一个模式变量可以将输入表达式的值绑定到该变量,变量可以在 => 右侧访问
1 | scala> "MON" match { |
- 类型模式:使用
模式变量: 类型
可以匹配输入表达式返回值的具体类型,需要注意的是备选模式的类型必须是输入表达式返回值类型的子类,否则会触发异常:error: scrutinee is incompatible with pattern type
1 | scala> val x: Int = 1 |
- 哨兵模式:在模式变量后面加上
if <boolean expression>
,可以为匹配表达式添加匹配条件,只有条件满足时才算匹配成功
1 | def showImportantNotification(notification: Notification, importantPeopleInfo: Seq[String]): String = { |
循环表达式/语句
for表达式
Scala 的for表达式是用于迭代的瑞士军刀,每次迭代会执行一个表达式,并返回所有表达式返回值的一个集合(可选)。
语法:enumerators 是一个枚举器,可以包含多个生成器(items <- items)和过滤器(if
1 | for (enumerators) [yield] <expression> |
执行:每次从枚举器中取出一个元素,执行表达式,返回所有返回值构成的一个集合(如果加了 yield 的话)。
示例:
1 | // 不带 yield,没有返回值 |
变形:
- 迭代器哨兵:枚举器中可以包含多个过滤器
1 | scala> for (i <- 1 to 10 if i % 2 == 0) yield {2 * i} |
- 迭代器嵌套:枚举器中可以包含多个迭代器
1 | scala> for {i <- 1 to 10 |
- 值绑定:在for循环中使用值绑定,可以把循环的大部分逻辑都集中在定义中,可以得到一个更为简洁的 yield 表达式
1 | scala> for { |
while语句
Scala 同样支持 while 和 do/while 循环语句,不过没有 for 表达式那么常用,因为它不是表达式,不能用来返回值。事实上,while 循环和 var通常是一起使用的,要想对程序产生任何效果,while循环通常要么更新一个var要么执行I/O。Scala 没有内建的 break 和 continue 语句,但可以通过 if 表达式来改写。
语法:
1 | // while |
执行:
- while 只要条件为true,循环体就会一遍接着一遍执行;
- do/while:一遍接着一遍执行循环体,直至条件为false
while 和 do/while语句也有自己的用途,比如需要不断读取外部输入知道没有可读的内容为止,不过Scala提供了很多更有表述性且功能更强的方法来处理循环。
try 表达式
异常传播机制:方法除了正常返回某个值外,也可以通过抛出异常终止执行,方法调用方要么捕获并处理这个异常,要么自我终止,让异常传播到更上层的方法调用方,异常通过这种方式传播,逐个展开调用栈,直至某个方法处理该异常或再没有更多方法为止。
抛出异常
语法:
1 | throw new classException("something") |
执行:抛出对应类型的异常,返回值类型为Nothing
1 | scala> throw new IllegalArgumentException("ddfs") |
捕获异常
语法:
1 | try { |
执行:
- try子句:首先执行代码体
,如果出现异常则先执行 catch 子句后再执行finally 子句,如果没有异常,则直接执行finally子句 - catch子句:根据try子句抛出的异常,依次尝试匹配每个模式,匹配成功则执行模式后面对应的表达式(使用方式和match表达式一致)
- finally子句:将那些无论是否抛出异常都想执行的代码以表达式的形式包在finally子句里,finally子句一般都是执行清理工作,这是正确关闭非内存资源的惯用做法,比如关闭文件、套接字、数据库连接
返回值:
- 如果没有抛出异常,返回try表达式子句的结果;
- 如果抛出异常且被捕获,则返回对应catch子句的结果;
- 如果抛出异常但没有被捕获,则整个表达式没有结果;
- 如果finally子句包含一个显式地返回语句,则整个表达式会返回finally子句的结果,否则按前三个规则
示例:
1 | import java.io.FileReader |