【问题标题】:Difference between Try vs. Try CatchTry 与 Try Catch 之间的区别
【发布时间】:2018-05-06 15:13:52
【问题描述】:

我了解 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 旨在提供某种类型的默认行为 失败的案例。

是否有任何示例可以说明如何使“管道、链、操作......”变得更容易?有什么例子可以说明为什么我们没有 try-catch 的相同好处?

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

【问题讨论】:

    标签: scala exception


    【解决方案1】:

    Try[A]

    这是一种代数数据类型 (ADT),由两种情况组成:Success[A]Failure[A]。这种代数结构定义了很多操作,如mapflatMap 等。 Try 有一个 map 和一个 flatMap 加上一个从 ATry[A] 的构造函数,这一事实使这个结构成为 Monad。

    Monad 是允许我们以函数式方式表达计算序列的抽象。我们来看一个例子

    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 理解来重写它

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

    try

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

    【讨论】:

      【解决方案2】:

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

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

      假设你有这个小程序:

      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?这不是因为你不能这样做,而是因为它不是很实用。如果您需要处理另一个线程中发生的异常(比如说,由一个演员),您无法捕获该异常,您可能希望向当前主线程传递一条消息,说明计算失败并且您将弄清楚如何处理。

      另外,为什么我们在Failure中返回divide?否则会出现类型不匹配?这是唯一的原因吗?

      它在Failure 中返回divide,因为它让您在程序失败时重新运行程序,但这只是程序员的决定。您可以只返回一个 Failure 包装某种错误消息。

      【讨论】:

        【解决方案3】:

        首先是关于 flatMap() 的一些背景知识:这个函数在很多方面都引用了众所周知的 monadic >= (bind) 运算符的概念,如果你想更深入地了解它,我强烈推荐阅读《Learn你是一个 Haskell for Great Good!”。

        但是简单地说,使用 Try 你可以做这样的事情:

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

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

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

        【讨论】:

          猜你喜欢
          • 2011-02-20
          • 2017-08-07
          • 1970-01-01
          • 2010-12-14
          • 2011-10-09
          • 2014-04-14
          • 1970-01-01
          • 2013-11-09
          • 2019-02-28
          相关资源
          最近更新 更多