【问题标题】:What advantages does scala.util.Try have over try..catch?scala.util.Try 比 try..catch 有什么优势?
【发布时间】:2017-04-11 18:33:42
【问题描述】:

在线搜索答案给出了两个突出的帖子(Codacy'sDaniel Westheide's),并且都给出了与 Scala's official documentation for Try 相同的答案:

上面示例中显示的 Try 的一个重要属性是它能够管道或链接操作,沿途捕获异常。

上面引用的例子是:

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
  }
}

但我可以使用传统的try 块轻松地进行流水线操作:

def divideConventional: Int = try {
  val dividend = StdIn.readLine("Enter an Int that you'd like to divide:\n").toInt
  val divisor = StdIn.readLine("Enter an Int that you'd like to divide by:\n").toInt
  val problem = dividend / divisor
  println("Result of " + dividend + "/"+ divisor +" is: " + problem)
  problem
} catch {
  case (e: Throwable) =>
    println("You must've divided by zero or entered something that's not an Int. Try again!")
    println("Info from the exception: " + e.getMessage)
    divideConventional
}

(注意:dividedivideConventional 在行为上略有不同,后者在出现问题的第一个迹象时会出错,但仅此而已。尝试输入“10a”作为dividend 的输入以查看我的内容意思。)

我想看看scala.util.Try 的流水线优势,但对我来说,这两种方法似乎是相等的。我错过了什么?

【问题讨论】:

  • 注意Try(...)只捕获非致命异常,所以它相当于`try { ... } catch { case NonFatal(e) => ... }
  • 请注意,Try 是类型系统的一部分。任何处理Try 类型值的代码都“知道”它可能是其中一个。可能抛出的例程可以将其包装在 Try 中并将其作为其他人的问题传递。没有Try,就没有简单的沟通方式,“嘿!需要其他人来处理这个问题。”
  • Daniel Westheide 在他的帖子中说:“如果您需要处理在其他线程上执行的 Actor 引发的异常,您显然无法通过捕获该异常来做到这一点。”我认为并发问题也很重要。

标签: scala try-catch monads codacy


【解决方案1】:

我认为您很难看到Try[T] 的组合能力,因为您在这两种情况下都在本地处理异常。如果您想通过额外的操作编写divideConventional 怎么办?

我们会有一些类似的:

def weNeedAnInt(i: Int) = i + 42

然后我们会有类似的东西:

weNeedAnInt(divideConventional())

但是,假设您想最大限度地允许用户输入重试次数(这通常是您在现实生活中的场景,您无法永远重新输入方法?我们必须另外用try-catch 包装weNeedAnInt 本身的调用:

try {
  weNeedAnInt(divideConventional())
} catch {
   case NonFatal(e) => // Handle?
}

但是如果我们使用divide,并且假设它没有在本地处理异常并向外传播内部异常:

def yetMoreIntsNeeded(i: Int) = i + 64

val result = divide.map(weNeedAnInt).map(yetMoreIntsNeeded) match {
  case Failure(e) => -1
  case Success(myInt) => myInt
}

println(s"Final output was: $result")

这不是更简单吗?也许,我认为这对答案有一定的主观性,我觉得它更干净。想象一下,我们有很长的此类操作流水线,我们可以将每个 Try[T] 组合到下一个,并且只有在流水线完成后才担心问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-04
    • 2017-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多