基本准则是对真正特殊的事情使用例外**。对于“普通”失败,最好使用Option
or Either
。如果您正在与 Java 交互,当有人以错误的方式打喷嚏时会引发异常,您可以使用它Try
来保证自己的安全。
让我们举一些例子。
假设您有一个从地图中获取某些东西的方法。会出什么问题?好吧,像段错误*堆栈溢出这样的戏剧性和危险的事情,或者像元素这样的预期没有找到。您会让segfault堆栈溢出引发异常,但如果您只是找不到元素,为什么不返回 anOption[V]
而不是值或异常(或null
)?
现在假设您正在编写一个用户应该输入文件名的程序。现在,如果您不只是在出现问题时立即放弃该程序,那么您可以Either
这样做:
def main(args: Array[String]) {
val f = {
if (args.length < 1) Left("No filename given")
else {
val file = new File(args(0))
if (!file.exists) Left("File does not exist: "+args(0))
else Right(file)
}
}
// ...
}
现在假设您要解析一个以空格分隔的数字的字符串。
val numbers = "1 2 3 fish 5 6" // Uh-oh
// numbers.split(" ").map(_.toInt) <- will throw exception!
val tried = numbers.split(" ").map(s => Try(s.toInt)) // Caught it!
val good = tried.collect{ case Success(n) => n }
因此,您有(至少)三种方法来处理不同类型的故障:Option
因为它有效/无效,在预期行为不有效的情况下,不是令人震惊和令人担忧的故障;Either
当事情可以工作或不工作时(或者,实际上,任何你有两个互斥选项的情况)并且你想保存一些关于哪里出了问题的信息;并且Try
当您不想自己处理异常的整个头痛,但仍需要与异常快乐的代码交互时。
顺便说一句,例外是很好的例子——所以你会在教科书或学习材料中发现它们比在其他地方更常见,我认为:教科书的例子通常是不完整的,这意味着通常可以通过精心设计来避免的严重问题应该而是通过抛出异常来标记。
*编辑:段错误使 JVM 崩溃,无论字节码如何,都不应该发生;那时即使是例外也无济于事。我的意思是堆栈溢出。
**编辑:异常(没有堆栈跟踪)也用于 Scala 中的控制流——它们实际上是一种非常有效的机制,它们可以启用库定义的break
语句和return
从您的方法返回的东西,即使控制实际上已经进入一个或多个闭包。大多数情况下,您自己不应该担心这一点,只是要意识到捕获所有 Throwable
s 并不是一个好主意,因为您可能会错误地捕获这些控制流异常之一。