以下是取自scala.util.Success的flatMap
的定义。
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
的值,并执行可能错误的操作。我不明白为什么我们需要将f
与try-catch
一起包装。以下是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
包装应用程序,这有什么必要?
答案 0 :(得分:4)
如果我理解你的问题,你会想知道为什么你需要(可能)加倍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: )
如果try/catch
中没有flatMap
,那么会发生什么事情会导致异常升级,从而导致首先出现Try
的全部内容。
答案 1 :(得分:1)
问题在于假设当函数的返回类型为Try[T]
时,它不会抛出任何异常。
您引用了对象Try {s} 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
只是普通类型。