位置:首页 > Scala > Scala 异常捕获怎么写:从 try/catch 到 Try、getOrElse 与 recover

Scala 异常捕获怎么写:从 try/catch 到 Try、getOrElse 与 recover

时间:2026-08-23  |  作者:宇宙开黑者  |  阅读:0

目录

  1. 传统写法:Scala 里的 try/catch/finally
  2. 用 Try 包装结果:Success 和 Failure
  3. 失败时给默认值:getOrElse
  4. 只恢复特定异常:recover
  5. 怎么选:try/catch 还是 Try

前言

在 Scala 里,异常处理语法和 Java 很接近,但一旦进入 RPC、Future 或其他异步链路,单纯依赖 try/catch 往往会让分支散落、代码变重。本文从基础捕获写起,再过渡到 scala.util.Try 的几种常见写法,帮助你判断:什么时候直接捕获异常,什么时候把失败显式包装成 Success/Failure,以及默认值和定向恢复该怎么落地。

在 Scala 里,异常处理语法和 Java 很接近,但一旦进入 RPC、Future 或其他异步链路,单纯依赖 try/catch 往往会让分支散落、代码变重。本文从基础捕获写起,再过渡到 scala.util.Try 的几种常见写法,帮助你判断:什么时候直接捕获异常,什么时候把失败显式包装成 Success/Failure,以及默认值和定向恢复该怎么落地。

传统写法:Scala 里的 try/catch/finally

Scala 支持和 Java 类似的 trycatchfinally 语义。不同点在于,catch 块里通常使用 case 来匹配异常类型。

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")
    }
  }  
}

这段代码会触发除零异常,因此输出结果如下:

/ by zero
run finally

为什么这种方式不总是最优

如果只是处理一个同步代码块,try/catch 已经足够直接。但在 RPC、异步调用或多层组合逻辑里,异常分支容易和业务分支交织在一起,代码会越来越难读。原文也明确指出,这类场景更推荐使用 Try 来承载成功或失败结果。

用 Try 包装结果:Success 和 Failure

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

这里有两个关键点:

  • 操作成功时,返回 Success[T]
  • 操作失败时,返回由 Failure 包装的异常对象。

也就是说,调用方不需要在外层再写一层异常捕获,而是可以通过模式匹配把成功和失败分支并列写清楚。

失败时给默认值:getOrElse

如果你的目标不是细分异常,而是“失败就兜底”,那 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

这个例子表达得很明确:

  • divide(6, 2) 成功,结果是 3
  • divide(6, 0) 失败,于是直接回退到默认值 -1

当业务只关心“能不能给出一个可继续使用的结果”时,这种写法通常比手动匹配 Success/Failure 更省事。

只恢复特定异常:recover

还有一种常见需求是:某些异常希望单独处理,其他情况再交给默认逻辑。这个时候可以使用 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 的逻辑。

使用 recover 时要看清返回值类型

recover 的作用不是单纯“打印错误”,而是把失败结果恢复成一个新的成功结果。因此你在 case 里返回的值,应该和原来的成功类型保持一致,便于后续继续使用。

从思路上看,recover 适合这种场景:异常可以分类处理,而且其中一部分异常有明确补偿策略。

怎么选:try/catch 还是 Try

结合这几种写法,可以这样理解:

用信息图展示 Scala 中 try/catch 与 Try 的选择关系,以及 Success、Failure、默认值处理之间的对应路径。
Scala 异常处理方式选择图把同步捕获、结果包装、默认值回退和定向恢复放到同一张图里,更容易判断不同场景该选哪种写法。
  • 临时处理单个同步代码块,使用 try/catch/finally 足够直接。
  • 希望把成功与失败统一建模,优先考虑 Try
  • 只要兜底结果,用 getOrElse
  • 只想恢复某些特定异常,用 recover

对 Scala 项目来说,Try 最大的价值不只是“能处理异常”,而是它把异常从隐式控制流,变成了可以匹配、组合和传递的显式结果。这也是它在异步和链式调用场景里更容易维护的原因。

免责声明:文中图文均来自网络,如有侵权请联系删除,心愿游戏发布此文仅为传递信息,不代表心愿游戏认同其观点或证实其描述。

相关文章

更多

精选合集

更多

大家都在玩

热门话题

大家都在看

更多