在Scala中,NonFatal和Exception有何区别?

40

这篇文章中提到:

如果你想捕获通常发生的“所有”错误,那么使用NonFatal:

import scala.util.control.NonFatal

try {
  operation()
} catch {
  case NonFatal(e) => errorHandler(e)
}

但我通常使用Exception

try {
  operation()
} catch {
  case e: Exception => errorHandler(e)
}

我想知道Scala中NonFatalException之间的区别是什么?在Scala中,Exception是否包括致命异常?

据我所知,在Java中,Exception用于非致命错误,而Error用于致命错误。Scala在Exception方面与Java有何不同?

哪种方法是捕获非致命异常的正确方式?

2个回答

40

编辑:已更新为最新的Scala版本(2.11+具有不同的NonFatal.apply定义)。


NonFatal只是一个方便的提取器,定义在scala.util.control中:

object NonFatal {
   /**
    * Returns true if the provided `Throwable` is to be considered non-fatal, or false if it is to be considered fatal
    */
   def apply(t: Throwable): Boolean = t match {
     // VirtualMachineError includes OutOfMemoryError and other fatal errors
     case _: VirtualMachineError | _: ThreadDeath | _: InterruptedException | _: LinkageError | _: ControlThrowable => false
     case _ => true
   }
  /**
   * Returns Some(t) if NonFatal(t) == true, otherwise None
   */
  def unapply(t: Throwable): Option[Throwable] = if (apply(t)) Some(t) else None
}

在JVM上没有特殊的“致命”异常 - Error并不总是“致命”的,它们只是一种特殊的内部异常。 “致命”异常只是NonFatal定义中使用的异常列表。在这个术语中,除了InterruptedException之外的所有Exception都被认为是非致命的。将InterruptedException视为致命的是有意义的,因为它意味着线程被中断,所以如果你想处理它,你应该显式地处理它。 NonFatal提取器也正确处理ControlThrowable。这些是由特殊的控制转移函数(如break内部的breakable)抛出的异常。

1
我仍不是很清楚,所以NonFatalException大致相同吗? - null
3
不,当你在 e: Exception 上匹配时,你正在检查异常的实际类型,就像 Java 中的 catch (Exception e)。如果异常是 Exception 的子类,则异常将被捕获。另一方面,NonFatal 是一个提取器对象,它对匹配的值执行额外的逻辑。在这种情况下,只有当 NonFatal.apply(e) 返回 true 时,NonFatal(e) 才会匹配(并捕获异常)。当它返回 true 时,你可以在上面的代码片段中看到。 - Vladimir Matveev
InterruptedException 会在什么情况下发生?在 Future{ .. } 块内的代码中,是否可能抛出 InterruptedException - null
3
为什么StackOverflow是非致命错误? - blueFast
1
@dangonfast 这在Scala 2.11(和2.12)中已经改变了;StackOverflow是致命的。请参阅NonFatal文档。还有更多信息,请参见问题跟踪器修复拉取请求和Google组讨论 - abc123
显示剩余5条评论

6

在Scala中,异常并不经常被提到,但它们仍然是处理意外故障时多次使用的内容。

当我们寻找何时捕获java.lang.Error?时,会出现多个答案和观点,但让我们专注于共同点。

一个合理的应用程序不应该尝试捕获

  • “Error是Throwable的一个子类,表示严重问题,合理的应用程序不应该尝试捕获它。”
  • “表示Java虚拟机已崩溃或已耗尽其继续运行所需的资源。”
  • “当Java虚拟机无法分配对象,因为它已经没有空闲内存,并且垃圾收集器也不能释放更多内存时抛出。”

NonFatal是非致命可投掷对象的提取器。将不匹配致命错误,例如VirtualMachineError(例如OutOfMemoryErrorStackOverflowErrorVirtualMachineError的子类),ThreadDeathLinkageErrorInterruptedException ControlThrowable,它们是一个合理的应用程序不应该尝试捕获的故障的一部分。

有了这个想法,我们可以编写代码来捕获所有无害的可投掷对象:

try {
  // dangerous code
} catch {
  case NonFatal(e) => log.error(e, "Something not that bad.")
}

如果我们看一下apply方法,就会非常清楚地看到它。

object NonFatal {
   /**
    * Returns true if the provided `Throwable` is to be considered non-fatal, or false if it is to be considered fatal
    */
   def apply(t: Throwable): Boolean = t match {
     // VirtualMachineError includes OutOfMemoryError and other fatal errors
     case _: VirtualMachineError | _: ThreadDeath | _: InterruptedException | _: LinkageError | _: ControlThrowable => false
     case _ => true
   }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接