为什么 scala.util.Success.flatMap (Try.flatMap) 在应用其参数后再次使用 try-catch?
Why does scala.util.Success.flatMap (Try.flatMap) use try-catch again after applying its argument?
以下是 flatMap
的定义,取自 scala.util.Success。
final case class Success[+T](value: T) extends Try[T] {
def flatMap[U](f: T => Try[U]): Try[U] =
try f(value)
catch {
case NonFatal(e) => Failure(e)
}
}
此处的 f
函数采用 T
类型的值,并执行可能错误的操作。我不明白的是为什么我们需要用 try-catch
包装 f
的应用程序。下面是 Try
:
伴随对象的 apply 方法
object Try {
def apply[T](r: => T): Try[T] =
try Success(r) catch {
case NonFatal(e) => Failure(e)
}
}
如您所见,该操作发生在一个Try
应用程序主体中,该应用程序主体已被try-catch
覆盖,并且不会抛出,而是安全地存储抛出的异常。所以在我看来,调用 f
(传递给 flatMap
的那个)应该是安全的(因为不会抛出异常)。
我的问题是为什么 flatMap
再次用 try-catch
包装应用程序,这有什么必要吗?
P.S:这是一个相关的问题,但没有回答我的问题:
Scala: how to understand the flatMap method of Try?
如果我正确理解你的问题,你想知道为什么你需要(可能)双倍 try/catch
,那么 f
可能会在返回 Try
之前抛出异常:
scala> import scala.util.Try
import scala.util.Try
scala> val someTry = Try(1)
someTry: scala.util.Try[Int] = Success(1)
scala> someTry.flatMap(theInt => {
| throw new Exception("")
| Try(2)
| })
res1: scala.util.Try[Int] = Failure(java.lang.Exception: )
如果 flatMap
中没有 try/catch
,这里会发生的情况是异常会升级,这破坏了首先拥有 Try
的全部意义。
问题在于假设当函数的 return 类型为 Try[T]
时,它不会抛出任何异常。
您引用了对象 Try 的 apply
方法的定义来证明它处理所有非致命异常。但是我们怎么知道用户定义的函数已经使用了它。
def test1(x: Int): Try[Int] = Try { if (x > 0) x
else throw new Exception("failed") }
// ideally all functions should be like test1.
def test2(x: Int): Try[Int] = if (x > 0) Success(x)
else Failure(new Exception("Failed"))
def test3(x: Int): Try[Int] = if (x > 0) Success(x)
else throw new Exception("Failed") // no Try.apply here.
// this compiles fine.
// since the type of else branch is Nothing, the overall return type is 'Try union Nothing' which is Try.
出于同样的原因,scala.concurrent.Future
还在其 flatMap 中使用了 try-catch:以提供具有 防弹 异常处理的操作链接。
它还表明,对于 scala 编译器,Try
或 Future
只是普通类型。
以下是 flatMap
的定义,取自 scala.util.Success。
final case class Success[+T](value: T) extends Try[T] {
def flatMap[U](f: T => Try[U]): Try[U] =
try f(value)
catch {
case NonFatal(e) => Failure(e)
}
}
此处的 f
函数采用 T
类型的值,并执行可能错误的操作。我不明白的是为什么我们需要用 try-catch
包装 f
的应用程序。下面是 Try
:
object Try {
def apply[T](r: => T): Try[T] =
try Success(r) catch {
case NonFatal(e) => Failure(e)
}
}
如您所见,该操作发生在一个Try
应用程序主体中,该应用程序主体已被try-catch
覆盖,并且不会抛出,而是安全地存储抛出的异常。所以在我看来,调用 f
(传递给 flatMap
的那个)应该是安全的(因为不会抛出异常)。
我的问题是为什么 flatMap
再次用 try-catch
包装应用程序,这有什么必要吗?
P.S:这是一个相关的问题,但没有回答我的问题: Scala: how to understand the flatMap method of Try?
如果我正确理解你的问题,你想知道为什么你需要(可能)双倍 try/catch
,那么 f
可能会在返回 Try
之前抛出异常:
scala> import scala.util.Try
import scala.util.Try
scala> val someTry = Try(1)
someTry: scala.util.Try[Int] = Success(1)
scala> someTry.flatMap(theInt => {
| throw new Exception("")
| Try(2)
| })
res1: scala.util.Try[Int] = Failure(java.lang.Exception: )
如果 flatMap
中没有 try/catch
,这里会发生的情况是异常会升级,这破坏了首先拥有 Try
的全部意义。
问题在于假设当函数的 return 类型为 Try[T]
时,它不会抛出任何异常。
您引用了对象 Try 的 apply
方法的定义来证明它处理所有非致命异常。但是我们怎么知道用户定义的函数已经使用了它。
def test1(x: Int): Try[Int] = Try { if (x > 0) x
else throw new Exception("failed") }
// ideally all functions should be like test1.
def test2(x: Int): Try[Int] = if (x > 0) Success(x)
else Failure(new Exception("Failed"))
def test3(x: Int): Try[Int] = if (x > 0) Success(x)
else throw new Exception("Failed") // no Try.apply here.
// this compiles fine.
// since the type of else branch is Nothing, the overall return type is 'Try union Nothing' which is Try.
出于同样的原因,scala.concurrent.Future
还在其 flatMap 中使用了 try-catch:以提供具有 防弹 异常处理的操作链接。
它还表明,对于 scala 编译器,Try
或 Future
只是普通类型。