【问题标题】:Is it safe to catch an Exception object捕获异常对象是否安全
【发布时间】:2019-05-30 19:49:18
【问题描述】:

我使用依赖异常的 Java 库。简化代码如下:

    try {
      val eventTime = eventTimeString.as[Date]
    } catch {
      case e: Exception =>
        logger.error(s"Can't parse eventTime from $eventTimeString", e)
        // take action for the bad Date string.
    }

在 Java 中,我只会捕获将字符串解析为 Date 的异常,而让其余的不被捕获,因为它们可能是致命的。我的理解是,捕获Exception 意味着捕获任何非致命/非严重异常。因为它与捕获Throwable 不同,所以它是安全的——但它真的是这样吗?使用它的基本原理是未知异常可能会从堆栈的更深处抛出,如果它们不是致命的,为什么不捕获所有它们。这一直是 Java 中的一个问题,很容易从您正在进行的直接调用中找到可能的异常,而不是从更深层次的调用中找到可能的异常。这是 Scala 解决方案,基本上意味着“捕获所有可恢复的异常”吗?

我的问题是;上面的代码是否被认为是良好的 Scala 风格,是否“安全”,这意味着比仅捕获字符串到日期转换异常要好。

【问题讨论】:

  • 如果你真的需要使用try / catch control,我建议你改为 catch NonFatal,因为 Exception 将包含致命错误(在 Java 中也是如此)。 - 但是,通常在 Scala 中,您会改用 Try monad。其中,内部将捕获任何非致命异常并提供功能更强大的 api 来与它们交互。
  • 是的,我已经看到了 monad 函数的用法,并且最终会转换。我在您的链接中看到,任何致命错误都不会创建Failure,也不会绕过Success。对于致命错误,这可能是我们在上面的代码中想要的。
  • 那么,这是否解决了您的问题? - 老实说,我仍然不清楚你的问题是什么。
  • 这个问题部分是关于风格,部分是关于使用Exception 作为一个包罗万象的。你两个都回答了,如果你把它放在答案中,我会给你信用。 @Mario Galic 提供了一个很好的教程,但这不是我要求的。
  • 我相信马里奥的回答比我写的更完整,他的回答中也包含了我提出的建议。最后还回答了 catchall 部分。所以我相信他的回答应该是公认的。不过,我很高兴我也提供了帮助 :)

标签: scala exception casting


【解决方案1】:

解决问题的风格方面,Try,正如@LuisMiguelMejiaSuarez 所建议的那样,提供了一种更惯用的 Scala 风格

Try(eventTimeString.as[Date]) match {
  case Success(eventTimeDate) => // work with eventTimeDate
  case Failure(e: IllegalArgumentException) => // work with e
  case Failure(e: NullPointerException) => // work with e
  ...
  case Failure(e) => // work with e
}

在语法上看起来并没有太大的不同,但在概念上却发生了很大的变化,因为 SuccessFailure 代表常规的,而不是一些特殊的控制结构 . Success 是一个值,类似于7 是值,而try-catch 更像是whileif-else 控制设施。

包装任何可能抛出 Try 的库调用,例如 Java 库提供的,我们可以使用 for-yield 糖来像这样链接调用

for {
  a <- Try(foo)
  b <- Try(bar)
  c <- Try(qux)
} yield {
  // work with a, b and c
}

在哪里

def foo: Int = {
  throw new TimeoutException("foo")
  42
}

def bar: String = {
  throw new IllegalArgumentException("bar")
  "hello"
}

def qux: Boolean = {
  throw new NullPointerException("qux")
  true
}

我们可以按顺序阅读这个链,而不必打断我们的思路,并尝试了解一些特殊的控制结构如何适应算法。

关于问题的安全方面,可以说,我们不应该捕获诸如LinkageError之类的致命异常,并且确实Try与以下异常不匹配

VirtualMachineError
ThreadDeath
InterruptedException
LinkageError
ControlThrowable

如图所示是constructed

object Try {
  def apply[T](r: => T): Try[T] =
    try Success(r) catch {
      case NonFatal(e) => Failure(e)
    }
}

NonFatal 在哪里

object NonFatal {
   def apply(t: Throwable): Boolean = t match {
     case _: VirtualMachineError | _: ThreadDeath | _: InterruptedException | _: LinkageError | _: ControlThrowable => false
     case _ => true
   }

  def unapply(t: Throwable): Option[Throwable] = if (apply(t)) Some(t) else None
}

【讨论】:

  • 所以这意味着Exception 将捕获NonFatal 中忽略的一些致命错误?
  • 您能否说明ExceptionNonFatal 的不同之处。这似乎很重要,目前还不太清楚。
  • NonFatal 捕获所有Throwables,除了上面提到的五个它在语义上认为是致命的。 Exception 扩展了Throwable,因此它比Throwable 低一级,并且它没有捕获任何Errors。例如,NonFatal 会捕获 AssertionErrorException 不会。
猜你喜欢
  • 2013-08-18
  • 2013-01-23
  • 1970-01-01
  • 2011-03-04
  • 1970-01-01
  • 1970-01-01
  • 2011-06-25
  • 2013-11-30
  • 1970-01-01
相关资源
最近更新 更多