Try与Try Catch之间的区别

时间:2018-05-06 15:13:52

标签: scala exception

我知道Try / Success / Failure与Try Catch之间存在差异。官方文件也有一个很好的例子:

import scala.io.StdIn
import scala.util.{Try, Success, Failure}

def divide: Try[Int] = {
  val dividend = Try(StdIn.readLine("Enter an Int that you'd like to divide:\n").toInt)
  val divisor = Try(StdIn.readLine("Enter an Int that you'd like to divide by:\n").toInt)
  val problem = dividend.flatMap(x => divisor.map(y => x/y))
  problem match {
    case Success(v) =>
      println("Result of " + dividend.get + "/"+ divisor.get +" is: " + v)
      Success(v)
    case Failure(e) =>
      println("You must've divided by zero or entered something that's not an Int. Try again!")
      println("Info from the exception: " + e.getMessage)
      divide <------------ Here!
  }
}

但是,我很难理解

  

以上示例中显示的Try的一个重要属性是它的能力   管道,链,操作,沿途捕捉异常。   上面的例子中的flatMap和map组合器各自基本上   将他们成功完成的价值,包裹在其中   成功类型由下一个组合器进一步操作   在链中,或者通常包含在Failure类型中的异常   简单地传递给链条。组合如恢复和   recoverWith旨在提供某种类型的默认行为   失败的情况。

是否有任何示例来说明如何使管道,连锁,运营更容易......&#34;?任何例子来说明为什么我们没有try-catch的相同好处?

另外,为什么我们会在divide中返回Failure?否则,我们会type mismatch?这是唯一的原因吗?

3 个答案:

答案 0 :(得分:5)

<强> Try[A]

这是一个由2个案例组成的代数数据类型(ADT):Success[A]Failure[A]。这个代数结构定义了许多操作,如mapflatMap等。 Try有一个map和一个flatMap以及一个ATry[A]的构造函数,这个结构就是Monad。

Monads是抽象,允许我们以功能方式表达计算序列。我们来看一个例子

def tryDivide(v1: Double, v2: Double): Try[Double] = Try(v1 / v2)

val tryDivisions = tryDivide(1.0, 2.0).flatMap {
   res => tryDivide(res, 2)
}.flatMap {
   res => tryDivide(res, 0)
}

作为此代码的结果,我们将获得Failure[A],因为最新的操作除以零。 Try是Monad的事实允许我们使用像这样的理解来重写它

for {
   v1 <- tryDivide(1.0, 2.0)
   v2 <- tryDivide(v1, 2)
   res <- tryDivide(v2, 0)
} yield res

<强> try

另一方面,try只是一种语言语法,它允许您捕获可能从代码中抛出的异常,但它不是具有可以执行的方法的代数结构。

答案 1 :(得分:1)

Try[A]表示一个计算,如果它成功的是A类型的值,否则出现错误并且是Throwable。它有两种形式:在成功的计算中,它是A类型的值,包装为Success[A]的实例。在失败时,它是Failure[A]的实例,它包含Throwable例外。

Try的一个特点是它允许您以非常智能的方式使用高阶函数(如OptionEither和其他monad)和链操作。 / p>

让我们说你有这个小程序:

scala> def divide(x: Int, y: Int): Try[Int] =
         if (y == 0) Try(throw new Exception("Error: division by zero!"))
         else Try(x / y)
divide: (x: Int, y: Int)scala.util.Try[Int]

你得到了

scala> divide(3,2)
res30: scala.util.Try[Int] = Success(1)

scala> divide(3,0)
res31: scala.util.Try[Int] = Failure(java.lang.Exception: Error: division by zero!)

因此,您可以使用高阶函数的功能同样以优雅的方式捕获故障:

scala> divide(3,0).getOrElse(0)
res32: Int = 0

这只是一个例子,但想想你是否尝试执行一个可能失败的更复杂的计算,你必须捕获这个结果并采取相应的行动。

为什么不在Scala中使用try catchs?这不是因为你无法做到这一点,而是因为它不是很有用。如果你需要处理另一个线程中发生的异常(比如演员),你就不能捕获该异常,你可能想要将消息传递给当前的主线程,说明计算失败了,你将弄清楚如何处理。

  

另外,为什么我们在失败中回归分裂?否则,我们会出现类型不匹配的情况?这是唯一的原因吗?

它会在divide中返回Failure,因为如果程序失败,它会让您重新运行程序,但这只是程序员做出的决定。您可能只是返回Failure包装某种错误消息。

答案 2 :(得分:0)

首先关于flatMap()的一些背景:这个函数在很多方面指的是monadic&gt; =(bind)运算符的众所周知的概念,如果你想更深入地了解它我强烈建议你阅读这本书& #34;为了大好而学习你的Haskell!&#34;。

但是简单地使用Try就可以做到这样的事情:

someTry.flatMap(//doSomething1).flatMap(//doSomething2)

然后进行一些匹配以从第一个或第二个函数获取值或获取错误(请注意,任何来自错误的flatMap都不会被计算出来并且错误将保持不变)但是你会用旧的方式做像这样的人:

try{
    //doSomething()
} catch {
    ...
}
try {
    //domSomething2()
} catch {
    ...
}