抛出异常
异常的抛出看上去与Java一模一样,首先创建一个异常对象然后用throw关键字抛出。但在scala里,throw也是有结果类型的表达式。下面举个有关结果类型的例子:package scalaTestobject Test6 { def main(args:Array[String]):Unit = { println(fun(9)) } def fun(n:Int) = { if(n % 2 == 0) n/2 else throw new RuntimeException("n must be even") }}
执行结果:
这段代码的意思是,如果n是偶数,将打印n的一半。如果n不是偶数,那么异常将被抛出。因此,无论怎么说,把抛出的异常当作任何类型的值都是安全的。任何使用经throw返回值的尝试都不会起作用,因此这样做不会有害处。
从技术角度上来说,抛出异常的类型是Nothing。尽管throw不实际产生任何值,你还是可以把它当作表达式。这种小技巧或许看上去很怪异,但像在上面这样的例子里却常常很有用。If的一个分支计算值,另一个抛出异常并得到Nothing。整个if表达式的类型就是那个实际计算值的分支的类型(Nothing类型将在以后会讲到)。捕获异常
package scalaTestimport java.io.FileReaderimport java.io.FileNotFoundExceptionimport java.io.IOExceptionobject Test6 { def main(args:Array[String]):Unit = { try { val f = new FileReader("input.txt") //使用并关闭文件,省略.................... }catch{ case ex:FileNotFoundException => { println("文件没有找到!") } case ex:IOException => println("IO异常") } }}
上面演示了捕获异常的语法。选择catch子句这种语法的原因是为了与scala很重要的部分:模式匹配保持一致,模式匹配是一种很强大的特征(我们在后面章节中会讲到)。
这个try-catch表达式的处理方式与其他语言中的异常处理一致。首先执行程序体,如果抛出异常,则依次尝试每个catch子句。本例中,如果异常是FileNotFoundException,那么第一个子句将被执行。如果是IOException类型,第二个子句将被执行。如果都不是,那么try-catch将终结把异常向上抛出去。注意:你将很快发现与java的差别是scala里不需要捕获检查异常,或把它们声明在throws子句中。如果愿意,你可以用@throws注解声明throws子句,但这不是必须的(这个后面会讲到)。finally子句
如果想让某些代码无论方法如何中止都要执行的话,可以把表达式放在finally子句里。如例:package scalaTestimport java.io.FileReaderimport java.io.FileNotFoundExceptionimport java.io.IOExceptionobject Test6{ def main(args:Array[String]):Unit = { try{ val f = new FileReader("input.txt") //省略....... }catch{ case ex:FileNotFoundException => { println("文件没有找到") } case ex:IOException => println("IO异常") }finally{ println("必须执行的语句") } }}
或者
package scalaTestimport java.io.FileReaderimport java.io.FileNotFoundExceptionimport java.io.IOExceptionobject Test6{ def main(args:Array[String]):Unit = { try{ val f = new FileReader("./.classpath") println(f.getEncoding()) //省略........... }finally{ println("必须执行的语句") } }}
注意:一般确保内存资源,如:文件、套接字、数据库连接等被关闭的惯例方式是在finally中进行处理,这跟在java中是一样的。Scala里还可以使用另一种被称为出借模式(loan pattern)的技巧更简洁地达到同样的目的(这个出借模式后面再讲)。
生成值
和其他大多数scala控制结构一样,try-catch-finally也产生值。如例:package scalaTestimport java.net.URLimport java.net.MalformedURLExceptionobject Test6 { def main(args:Array[String]):Unit = { println(urlFor("ddd")) } def urlFor(path:String) = { try{ new URL(path) }catch{ case e:MalformedURLException => new URL("http://www.scala-lang.org") } }}
结果:
上例演示了如何尝试拆分URL,但如果URL格式错误就使用默认值。也就是,如果没有异常抛出,则对应于try子句;如果抛出异常并被捕获,则对应于相应的catch子句。如果异常被抛出但没被捕获,表达式就没有返回值。
由finally子句计算得到的值,即使有也会被抛弃。通常finally子句做一些诸如关闭文件之类的清理工作,它们不应该修改主函数体或catch子句中计算的值。Scala的行为与java的差别仅在于java的try-finally不产生值。在java里,如果finally子句包含返回语句,或抛出一个异常,这个返回值或异常将“凌驾”于任何之前在try代码块或某个catch子句里产生的值或异常之上。我们来测试一下,scala的finally中定义返回语句的情况,如下例:package scalaTestobject Test6{ def main(args:Array[String]):Unit = { println(f) } def f() = { try{1}finally{2} }}
结果:
我们再来看看另一个很特别的例子:
package scalaTestobject Test6 { def main(args:Array[String]):Unit = { println(f) } def f():Int = { try{return 1}finally{return 2} }}
(注: scala中使用return进行返回值的时候,方法上一定要注明返回类型!!!),我们再来看看结果(很震惊!!!!!!相当震惊!!!!):
这两个例子足以令大多数程序员震惊!!!因此通常最好还是避免用finally子句返回值,而是把它理解为确保某些操作发生的途径,如关闭打开文件。