简介
Scala是一种微妙的野兽,你需要注意它给出的警告。大多说java和scala程序员都听说过捕捉可抛出的、所有异常的父类,是很邪恶的,像下面这种模式是应该拒绝的:
try {
aDangerousFunction()
} catch {
case ex: Throwable => println(ex)
// Or even worse
case ex => println(ex)
}
这种模式是非常危险的,一下将介绍原因。
问题所在
在java中,捕获一切可抛出异常会做出一些非常有危险的事,比如阻止JVM抛出StackOverflowError或OutOfMemoryError异常。这不是很理想但也不是灾难性的。
在scala中,它会更令人发指。scala中使用了异常从嵌套闭包返回。考虑以下代码:
def inlineMeAgain[T](f: => T): T = {
f
}
def inlineme(f: => Int): Int = {
try {
inlineMeAgain {
return f
}
} catch {
case ex: Throwable => 5
}
}
def doStuff {
val res = inlineme {
10
}
println("we got: " + res + ". should be 10")
}
doStuff
我们在两个嵌套的闭包内部使用一个return语句。这似乎是一个比较模糊的边缘情况,但在实践中是可以肯定会出现的。为了解决这个问题,scala编译器会抛出一个NonLocalReturnControl异常。
不幸的是,这是一个可捕获异常,因此,上述代码会打印5而不是预期的10。
解决方法
虽然我们可以说“不要捕获Throwable”直到我们脸色发青,有的时候你真的需要确认真的绝对没有异常通过。你可以在任何你想捕获那些其他的类型异常的地方捕获Throwable,但这非常繁琐并且容易出错。
幸运的是这其实很容易处理,这要归功于Scala的重点实施多语言的无魔法“catch”,在try-catch的部分只是一些糖超过的部分功能,我们可以定义的部分功能!
def safely[T](handler: PartialFunction[Throwable, T]): PartialFunction[Throwable, T] = {
case ex: ControlThrowable => throw ex
// case ex: OutOfMemoryError (Assorted other nasty exceptions you don't want to catch)
//If it's an exception they handle, pass it on
case ex: Throwable if handler.isDefinedAt(ex) => handler(ex)
// If they didn't handle it, rethrow. This line isn't necessary, just for clarity
case ex: Throwable => throw ex
}
// Usage:
/*
def doSomething: Unit = {
try {
somethingDangerous
} catch safely {
ex: Throwable => println("AHHH")
}
}
*/
定义一个safely函数,它需要提供一个partial函数然后生成另一个partial函数。现在,通过简单的使用 catch safely { / catch block / },我们可以自由安全的捕获异常,或者其他任何Throwable,并且把其他一些邪恶类型的异常限制在代码的其他部分。