Scala:处理Java库异常的最佳方法?

时间:2012-02-15 10:25:59

标签: scala exception exception-handling scala-java-interop

假设我正在使用Scala项目中的Java库。 Java库在所有地方都抛出了异常,但我觉得让它们“在Scala世界中”传播感觉不舒服,因为无法确定Scala方法可以抛出什么异常(除非记录它们)。所以这就是我倾向于编写的代码:

def doesNotThrowExceptions(parameter: String): Either[Throwable, T] =
  catching(classOf[IOException], classOf[NoSuchAlgorithmException]) either {
    // Code calling the Java library
    // Code generating a value of type T
  }

然后,通常,我会使用Either.RightProjection.flatMap链接返回Either[Throwable, ...]Either.RightProjection.map的方法,以混合返回Either[Throwable, ...]的方法和其他方法。或者只是Either.fold使用Throwable值执行某些操作。但不知怎的,这仍然感觉不完全正确

这是处理Scala中Java异常的最“惯用”方式吗?是不是有更好的方法?

2 个答案:

答案 0 :(得分:23)

我不确定是否有单一最常用的处理Java异常的方法,因为至少有四种不同的情况会被抛出:

  1. 你和图书馆设计师都没想出错的东西出了问题。
  2. 图书馆设计师希望工作的东西没有,你需要知道细节。
  3. 你希望工作的东西没有,你不需要知道细节。
  4. 有时候这个方法产生一个值,有时候不会产生一个值,并通过抛出异常来传达它。
  5. 对于每种情况,最佳做法都有所不同

    1。真正例外的例外

    Scala具有功能齐全的异常处理功能。让一个意想不到的异常传播没有任何问题,直到你处于一个可以对它做某事的水平。将每个可能的例外打包到Either可能会浪费大量时间。只记录你知道你没有处理的内容,并在适当的高级别使用try / catch(例如,saveEverything方法应该放在try / catch块中(或将其内容包装在一个中),因为无论如何出了什么问题,如果保存一切都失败了你可能想要挽救局面,而不仅仅是死亡。

    特别是,您可能希望以这种方式处理Error,并且只将Exception而不是所有Throwable打包到Either

    2。您需要了解的有关

    的例外情况

    这就是你所说的情况,你已经就如何处理它们提出了一些很好的建议。您已经注意到,您可以使用catching将例外打包到Either。然后你也可以

    一个。使用模式匹配,可以让您更深入地划分Either

    doesNotThrowExceptions("par").right.map(transformData) match {
      case Left(ioe: IOException) => /* ... */
      case Left(nsae: NoSuchAlgorithmException) => /* ... */
      case Right(x) => /* ... */
      case Left(e) => throw e  // Didn't expect this one...
    }
    

    湾记录错误后下转换为Option

    doesNotThrowExceptions("par").left.map{ e =>
      println("You're not going to like this, but something bad happened:")
      println(e)
      println("Let's see if we can still make this work....")
    }.right.toOption
    

    ℃。如果异常是流量控制的一个非常重要的部分,Either可能还不够。您可能希望定义自己的Either类课程,而不仅仅是LeftRight。或者您可以嵌套Either的左侧:

    try { Right(/* java code */) }
    catch {
      case ioe: IOException => Left(Left(ioe))
      case nsae: NoSuchAlgorithmException => Left(Right(nsae))
    }
    

    d。使用Scalaz Validation,它与Either非常相似,但对异常处理的定制更多。

    3。只需要知道出错的例外情况,

    4。抛出异常表示无返回值

    即使这些在概念上是两个不同的类别,你也可以用同样的方式处理它们:

    catching(classOf[IOException], classOf[NoSuchAlgorithmException]) opt { ... }
    

    获得Option回复。然后是mapflatMap等。

答案 1 :(得分:6)

我总是更喜欢scalaz.Validation而不是scala.Either。这两个是代数相同的结构,但前者功能更丰富(有更多有用的方法和类型类实例)。

检查this link以获得Chris Marshall在scalaz.Validation和其他Scalaz礼物上的精彩演示。

为了与使用经典异常处理机制的代码交谈,我定义了以下丰富方法和类型类实例:

implicit def catchW[A](underlying: Catch[A]) = new CatchW(underlying)

class CatchW[A](underlying: Catch[A]) {
  def validation(body: => A): Validation[Throwable, A] = {
    underlying.withApply(_.fail)(body.success)
  }

  def vnel(body: => A): ValidationNEL[Throwable, A] = {
    underlying.withApply(_.failNel)(body.success)
  }
}

implicit def catchMonoid[A] = new Monoid[Catch[A]] {
  val zero = noCatch
  def append(x: Catch[A], y: => Catch[A]) = x or y
}