13.1 异常处理

Scala支持Java的异常处理语义,但是,它有着不同的语法。在Scala里,可以像Java那样抛出异常①:

①实例化时,可以忽略空的括号。

  1. throw new WhatEverException

也可以像Java里那样放一个try。不过,Scala并不强制捕获不关心的异常——即便是受控异常也不必捕获它。这样就无需在代码里添加不必要的catch块——只要让那些不关心的异常沿着调用链传播上去即可。所以,举个例子,如果想调用Threadsleep(),不用这样做:

  1. // Java code
  2. try {
  3. Thread.sleep(1000);
  4. }
  5. catch(InterruptedException ex) {
  6. // Losing sleep over what to do here?
  7. }

只要写成这样即可:

  1. Thread.sleep(1000)

Scala不会固执地强求不必要的try-catch块了。

当然,对于某些可以为之做些什么的异常,我们肯定是要处理的——这才是catch的意义所在。Scala的catch语法有着很大的不同;我们可以使用模式匹配来处理异常,看个例子:

ScalaForTheJavaEyes/ExceptionHandling.scala

  1. def taxFor(amount: Double) : String = {
  2. if (amount < 0)
  3. throw new IllegalArgumentException("Amount must be greater than zero")
  4. if (amount < 0.1) throw new RuntimeException("Amount too small to be taxed")
  5. if (amount > 1000000) throw new Exception("Amount too large...")
  6. "Tax for $" + amount + " is $"+ amount * 0.08
  7. }
  8. for (amount <- List(100.0, 0.09, -2.0, 1000001.0)) {
  9. try {
  10. println(taxFor(amount))
  11. }
  12. catch {
  13. case ex: IllegalArgumentException => println(ex.getMessage())
  14. case ex: RuntimeException => {
  15. // if you need a block of code to handle exception
  16. println("Don't bother reporting..." + ex.getMessage())
  17. }
  18. }
  19. }

上面的代码输出(带有部分栈追踪)如下:

  1. Tax for $100.0 is $8.0
  2. Don't bother reporting...Amount too small to be taxed
  3. Amount must be greater than zero
  4. java.lang.Exception: Amount too large...
  5. at Main$$anon$1.taxFor((virtual file):9)
  6. at Main$$anon$1$$anonfun$1.apply((virtual file):15)
  7. at Main$$anon$1$$anonfun$1.apply((virtual file):13)
  8. at scala.List.foreach(List.scala:841)
  9. at Main$$anon$1.<init>((virtual file):13)
  10. at Main$.main((virtual file):4)
  11. ...

taxFor()方法根据输入抛出三种不同的异常。catch块用case语句处理两个异常。上面的输出展示了代码块如何处理这两个异常。第三个未处理的异常导致程序的终结,并打印出stack trace的细节。case语句的顺序很重要,请参考13.2节,“注意catch的顺序”中介绍的内容。

Scala也支持finally块——就像Java一样,无论try块的代码是否抛出异常,它都会执行。

在上面的代码里,我们见识到了如何处理特定的异常。如果想捕获所有的异常,可以用_(下划线)做case条件,如下面例子所示:

ScalaForTheJavaEyes/CatchAll.scala

  1. def taxFor(amount: Double) : String = {
  2. if (amount < 0)
  3. throw new IllegalArgumentException("Amount must be greater than zero")
  4. if (amount < 0.1) throw new RuntimeException("Amount too small to be taxed")
  5. if (amount > 1000000) throw new Exception("Amount too large...")
  6. "Tax for $" + amount + " is $"+ amount * 0.08
  7. }
  8. for (amount <- List(100.0, 0.09, -2.0, 1000001.0)) {
  9. try {
  10. println(taxFor(amount))
  11. }
  12. catch {
  13. case ex : IllegalArgumentException => println(ex.getMessage())
  14. case _ => println("Something went wrong")
  15. }
  16. }

上面代码的输出如下。除有自己特殊catch块的IllegalArgumentException外,捕获所有(catchall)的case捕获了的所有情况:

  1. Tax for $100.0 is $8.0
  2. Something went wrong
  3. Amount must be greater than zero
  4. Something went wrong

在Scala里,如同捕获受控异常是可选的一样,声明受控异常也是可选的。Scala并不需要声明抛出什么异常。参见11.4节“继承类”以了解与Java代码互操作相关的问题。