【问题标题】:Quick failure when using for-comprehension with scala.util.Try使用 scala.util.Try 进行理解时快速失败
【发布时间】:2013-02-07 13:23:52
【问题描述】:

我真的很喜欢 Scala 2.10 中的 scala.util.Try,它与 for-comprehension 一起工作的方式使得处理多个可能很容易出错的步骤。

例如,我们可以使用以下代码来确保仅当且仅当一切都在控制之中并且我们正确获取值时才打印出这两个数字。

def tryA: Try[Int] = {....}
def tryB: Try[Int] = {....}

for {
  a <- tryA
  b <- tryB
} { 
  println (s"We got:${a+b}")
}

但我担心的一个问题是这段代码实际上忽略了任何异常,这意味着它看起来像下面的 try-cactch 块:

try {
  // .....
} catch {
  case _: Exception => // Swallow any exception
}

据我所知,有一种说法是这种代码是难闻的,因为没有人会注意到发生了异常。

我想要实现的是仍然使用for来确保println只有在一切正常的情况下才执行,但是如果任何步骤有任何异常,它都会炸毁并直接抛出异常.

目前我是这样做的,但它似乎不太优雅,因为它引入了一个新的Try[Unit] 对象,所以我想知道如何才能使这段代码更好?

例如,是否可以去掉result 变量和result.get 语句,但仍然抛出异常?

def tryA: Try[Int] = {....}
def tryB: Try[Int] = {....}

val result = for {
  a <- tryA
  b <- tryB
} yield { 
  println (s"We got:${a+b}")
}
result.get

更新

为了让事情更清楚,这是这个问题中第一个代码的 Scala REPL 的结果。

scala> def tryA: Try[Int] = Success(1)
tryA: scala.util.Try[Int]

scala> def tryB: Try[Int] = Failure(new Exception("error"))
tryB: scala.util.Try[Int]

scala> for {
     |   a <- tryA
     |   b <- tryB
     | } { 
     |   println (s"We got:${a+b}")
     | }

scala> 

我们可以看到这里什么都没有发生,即使 tryBFailure 也有例外。我想得到的是抛出异常,并且没有引入带有yield 的新Try[Unit] 对象,这可能吗?

【问题讨论】:

  • println 的类型是Unit,所以result 要么抛出异常,要么返回Unit()
  • 不,除非我使用get 方法,否则它不会抛出异常。
  • 您的示例代码包含一个无条件的result.get。 (我的意思是在我的第一条评论中写result.get...)

标签: scala exception try-catch


【解决方案1】:

你可以使用recover:

import scala.util.Try

def tryEven = Try { val i = (math.random * 1000).toInt; if (i % 2 != 0) throw new Exception("odd") else i }

def tryEvenOrNeg1 = Try { val i = (math.random * 1000).toInt; if (i % 2 != 0) throw new Exception("odd") else i } recover { case exx: Exception => -1 }

scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
res1: scala.util.Try[Unit] = Failure(java.lang.Exception: odd)

scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
res2: scala.util.Try[Unit] = Failure(java.lang.Exception: odd)

scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
res3: scala.util.Try[Unit] = Failure(java.lang.Exception: odd)

scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
Got 542, -1

scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
res5: scala.util.Try[Unit] = Failure(java.lang.Exception: odd)

scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
res6: scala.util.Try[Unit] = Failure(java.lang.Exception: odd)

scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
Got 692, 750

我删除了反映Success(())resNN

【讨论】:

  • 我要做的不是获取默认值或挽救异常。相反,如果tryEventryEvenOrNeg1 是失败,我希望直接抛出异常。也就是说,我想在我的帖子中实现result.get的效果,但是没有result这个变量。
  • (for (a &lt;- tryEven; b &lt;- tryEven) yield println(s"Got $a $b")).get
【解决方案2】:

好吧,我忘了我们在 Scala 中总是有隐式转换。 ;-)

所以我们可以自己实现这个行为,它会比yield 版本创建更多的对象,但我认为这段代码的意图要清楚得多。

implicit class BlowUpTry[T](current: Try[T])  {

  def throwIfFailed: Try[T] = current match {
    case Success(value)     => current
    case Failure(exception) => throw exception
  }

}

def tryA: Try[Int] = Success(1)
def tryB: Try[Int] = Failure(new Exception("error"))

for {
  a <- tryA.throwIfFailed
  b <- tryB.throwIfFailed
} {
  println(s"We got ${a + b}")
}

【讨论】:

  • 如果要抛出异常,使用Try 毫无意义。
  • 加上throwIfFailed相当于Try的“get”方法。
猜你喜欢
  • 2012-06-22
  • 2018-09-26
  • 2019-10-31
  • 2012-08-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-19
  • 2012-06-21
相关资源
最近更新 更多