Scala 中的异常捕获

1,233 阅读1分钟

异常捕获

scala 和 Java 类似,也有try & catch 语义来捕获异常。scala 的异常捕获如下:

object TestFuture {
  def main(args: Array[String]): Unit = {
    var num = 1
    try {
      num = 100/0  // 生成算数异常
    } catch {
      // :type 表示是否匹配 type 类型
      case e: ArithmeticException => println(e.getMessage)
      case e: RuntimeException => println(e.getMessage)
      // ... 其他方式一并用 case 处理
    } finally {
      // .. 不论是否处理异常,都会执行这个代码块
      println("run finally")
    }
  }  
}

catch块中,我们使用case来匹配可能出现的异常情况,上述代码的输出为:

/ by zero
run finally

try .. catch 方式虽然能捕获,但是实现方式不优雅,如果是在 rpc 等异步环境下,代码结构会非常混乱。因此,我们更推荐使用下面的Try 方式处理。

Try 方式

给出Try 的最基本使用方式:

import scala.util.{Failure, Success, Try}

object TestFuture {
  def main(args: Array[String]): Unit = {
    divide(1, 2) match {
      case Success(num) => println(num)
      case Failure(e) => println(e.getMessage)
    }

    divide(1, 0) match {
      case Success(num) => println(num)
      case Failure(e) => println(e.getMessage)
    }
  }

  def divide(a: Int, b: Int): Try[Int] = {
    b match {
      case 0 => Failure(new ArithmeticException(s"$a divide by zero"))
      case _ => Success(a / b)
    }
  }
}

输出结果:

0
1 divide by zero

我们可以看出match方式,如果成功则返回Success[T] 的类型,否则返回Failure包装的Exception

如果我们想要在失败时候获取默认值,可以使用getOrElse 的方式。举个例子:

import scala.util.{Failure, Success, Try}

object TestFuture {
  def main(args: Array[String]): Unit = {
    val result1= divide(6, 2) getOrElse -1
    val result2 = divide(6, 0) getOrElse -1
    println(s"result1 = $result1, result2 = $result2")
  }

  def divide(a: Int, b: Int): Try[Int] = {
    b match {
      case 0 => Failure(new ArithmeticException(s"$a divide by zero"))
      case _ => Success(a / b)
    }
  }
}

此时输出结果:

result1 = 3, result2 = -1

假设我们想单独处理一些特殊的异常,剩下的按照默认值处理,则可以使用recover 的方式,代码示例:

import scala.util.{Failure, Success, Try}

object TestFuture {
  def main(args: Array[String]): Unit = {
    val result = divide(1, 2) recover {
      case e: ArithmeticException => println(e.getMessage)
      case e: RuntimeException => println(e.getMessage)
      case _ => "recover failed"
    }
    println(result.get)
  }

  def divide(a: Int, b: Int): Try[Int] = {
    b match {
      case 0 => Failure(new ArithmeticException(s"$a divide by zero"))
      case -1 => Failure(new RuntimeException("runtime failed"))
      case _ => Success(a / b)
    }
  }
}

注意,如果没有异常,则返回Success[T],只有失败的时候才会走Recover 的逻辑