scala's尝试优雅的错误行为

时间:2014-02-04 15:19:05

标签: scala refactoring try-catch

在scala中有更优雅的方法吗?

def doTheDangerousThing(): Try[Result] = {
  val result = Try(dangerousOp)

  if (result.isFailure) {
     println("error")
  }

  result
}

9 个答案:

答案 0 :(得分:13)

我认为你的if语句完全有效。这是另一种选择:

def doTheDangerousThing(): Try[Result] = Try(dangerousOp) recoverWith {
    case exception => println("error"); Failure(exception)
}

答案 1 :(得分:4)

这样的事情:

   def doTheDangerousThing[Result](dangerousOp: =>Result): Try[Result] = Try(dangerousOp) match {
    case o @ Failure(_) =>  println("error"); o
    case _ => _
  }

答案 2 :(得分:4)

不确定这是否更具惯用性,但有时我发现以这种方式放置recoverWith会提高我的可读性:

def doDangerousThing(): Try[Result] = Try {
  dangerousOp
} recoverWith {
  case t: Throwable => println("error"); Failure(t)
}

答案 3 :(得分:2)

我的首选,

def doTheDangerousThing(): Option[Result] = Try (dangerousOp) toOption

如果Try成功,如果Some(value)失败,您将获得None

有关Try次使用的大型编辑,请查看Try introduced in Scala 2.10.0

答案 4 :(得分:2)

有办法。例如:

def doTheDangerousThing(): Try[Result] = {
  val result = Try(dangerousOp)

  result.failed foreach { _ =>
     println("error")
  }

  result
}

或者,如果您不想重复result,那么:

def doTheDangerousThing(): Try[Result] = {
  Try(dangerousOp) recover {
    case ex => println("error"); throw ex
  }
}

答案 5 :(得分:1)

在某些情况下,我喜欢使用两步法,这将允许我更细粒度的错误信息控制:

  def retrieveData(dataId: String): Try[String] = {
    Try {
      Option(someApi(dataId))
        .getOrElse(throw SomeApiFailedException("invalid dataId"))
    } recoverWith {
      case e: SomeApiFailedException => Failure(e)
      case e: Throwable => Failure(SomeApiFailedException("failed retrieve dataId"))
    }
  }

  case class SomeApiFailedException(err: String) extends RuntimeException(err)

答案 6 :(得分:0)

好吧,我想你可以这样做:

def doTheDangerousThing(): Option[Result] = 
  Try(dangerousOp) match {
    case Success(result) => Some(result)
    case Failure(e) => None //might want to log the error as well
  }

答案 7 :(得分:0)

我可以从三种实现中选择一种,具体取决于我是否:

  • 只需向上传播(doTheDangerousThing1
  • 即可
  • 忽略错误(doTheDangerousThing2
  • 在向上传播错误(doTheDangerousThing3
  • 时拦截错误

以下是代码:

import scala.util.{Try,Success,Failure}
object temp {
  type Result = Int

  def dangerousOp = {
    val r = scala.util.Random.nextInt(10)
    if (r > 5) r else throw new RuntimeException("Failed on " + r)
  }
  def logMessage[T](t: T) = println(t)

  def doTheDangerousThing1(): Try[Result] = Try(dangerousOp)

  def doTheDangerousThing2(): Option[Result] = {
    Try(dangerousOp) match {
      case Success(r) => Option(r)
      case _ => None
    }
  }


  def doTheDangerousThing3(): Try[Result] = {
    Try(dangerousOp) match {
      case t @ Success(r) => t
      case t @ _ => logMessage("failed: "+t); t
    }
  }
}

在REPL内部

scala> doTheDangerousThing1
res0: scala.util.Try[Result] = Success(9)

scala> doTheDangerousThing1
res1: scala.util.Try[Result] = Success(9)

scala> doTheDangerousThing2
res2: Option[Result] = None

scala> doTheDangerousThing2
res3: Option[Result] = Some(7)

scala> doTheDangerousThing3
failed: Failure(java.lang.RuntimeException: Failed on 0)
res4: scala.util.Try[Result] = Failure(java.lang.RuntimeException: Failed on 0)

scala> doTheDangerousThing3
failed: Failure(java.lang.RuntimeException: Failed on 0)
res5: scala.util.Try[Result] = Failure(java.lang.RuntimeException: Failed on 0)

答案 8 :(得分:0)

添加像foreach这样的方法但是像@vptheron这样的失败的隐式类会看起来像这样

implicit class EnhancedTry[T](t: Try[T]) {
  def forfailed[U](f: Throwable => U): Unit = t match {
    case Failure(e) => f(e)
    case _ => ()
  }
}

然后我们可以做

val t = Try {
  throw new Exception("An error")
}

t.forfailed(e => println(e.getMessage))

// or use pattern matching

t.forfailed {
  case e : NullPointerException => println("NPE " + e.getMessage)
  case e => println("Other " + e.getMessage)
}

f: Throwable => U的类型也可以是Throwable => Unit,但我已经模仿foreach上函数参数的返回类型。 我无法弄清楚为什么它有一个泛型返回类型而不是Unit。