为什么 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 编译器,TryFuture 只是普通类型。