25

假设我正在使用 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 异常的最“惯用”方式吗?没有更好的方法吗?

4

2 回答 2

23

我不确定是否有一种惯用的方式来处理 Java 的异常,因为它们至少会抛出四种不同的情况:

  1. 你和图书馆设计者都没有真正预料到会出错的事情出错了。
  2. 图书馆设计者希望能起作用的东西没有,你需要知道细节。
  3. 你希望的东西没有用,你不需要知道细节。
  4. 有时该方法会产生一个值,有时不会,它通过抛出异常来传达该值。

每种情况下的最佳实践可以说是不同的

1. 真正异常的例外

Scala 具有功能齐全的异常处理。让意外的异常传播而未被捕获并没有错,直到您达到可以对其采取措施的水平。将每一个可能的异常打包成一个Either可能会浪费很多时间。只需记录您知道您没有处理的内容,并在适当的高级别使用 try/catch(例如,saveEverything方法可能应该进入 try/catch 块(或将其内容包装在一个块中),因为无论出现什么问题,如果保存一切都失败了,您可能想尝试挽救局面,而不仅仅是死)。

特别是,您可能希望以Error这种方式处理,并且只将 s 打包Exception,而不是所有Throwables,放入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

C。如果异常是您的流程控制中非常重要的一部分,那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。然后map, flatMap, 等等..

于 2012-02-15T11:33:14.273 回答
6

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

检查此链接以获取 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
}
于 2012-02-15T12:59:52.443 回答