假设我正在使用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异常的最“惯用”方式吗?是不是有更好的方法?
答案 0 :(得分:23)
我不确定是否有单一最常用的处理Java异常的方法,因为至少有四种不同的情况会被抛出:
对于每种情况,最佳做法都有所不同
Scala具有功能齐全的异常处理功能。让一个意想不到的异常传播没有任何问题,直到你处于一个可以对它做某事的水平。将每个可能的例外打包到Either
可能会浪费大量时间。只记录你知道你没有处理的内容,并在适当的高级别使用try / catch(例如,saveEverything
方法应该放在try / catch块中(或将其内容包装在一个中),因为无论如何出了什么问题,如果保存一切都失败了你可能想要挽救局面,而不仅仅是死亡。
特别是,您可能希望以这种方式处理Error
,并且只将Exception
而不是所有Throwable
打包到Either
。
这就是你所说的情况,你已经就如何处理它们提出了一些很好的建议。您已经注意到,您可以使用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
类课程,而不仅仅是Left
和Right
。或者您可以嵌套Either
的左侧:
try { Right(/* java code */) }
catch {
case ioe: IOException => Left(Left(ioe))
case nsae: NoSuchAlgorithmException => Left(Right(nsae))
}
d。使用Scalaz Validation
,它与Either
非常相似,但对异常处理的定制更多。
即使这些在概念上是两个不同的类别,你也可以用同样的方式处理它们:
catching(classOf[IOException], classOf[NoSuchAlgorithmException]) opt { ... }
获得Option
回复。然后是map
,flatMap
等。
答案 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
}