我使用依赖异常的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中一直是一个问题,在Java中很容易从您进行的直接调用中找到可能的异常,而从更深层的调用中很难找到。难道这就是Scala解决方案的基本含义,即“捕获所有可恢复的异常”?
我的问题是;是上面的代码被认为是良好的Scala风格,是否是“安全”的呢,这比仅将字符串捕获到Date强制转换异常要好。
答案 0 :(得分:6)
@LuisMiguelMejiaSuarez提出的解决问题Try
的样式方面,提供了一种更惯用的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
}
从语法上看,并没有太大区别,但是从概念上讲,这是一个很大的转变,因为Success
和Failure
代表规则的值,而不是某些特殊的控制结构。 Success
是一个值,就像7
是一个值,而try-catch
更像是while
或if-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
所示
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
}