Scala Try 与错误处理

当你在尝试一门新的语言时,可能不会过于关注程序出错的问题, 但当真的去创造可用的代码时,就不能再忽视代码中的可能产生的错误和异常了。 鉴于各种各样的原因,人们往往低估了语言对错误处理支持程度的重要性。

事实会表明,Scala 能够很优雅的处理此类问题, 这一部分,我会介绍 Scala 基于 Try 的错误处理机制,以及这背后的原因。 我将使用一个在 Scala 2.10 新引入的特性,该特性向 2.9.3 兼容, 因此,请确保你的 Scala 版本不低于 2.9.3。

二.异常抛出与捕获 2.1 其他语言的错误处理机制

在介绍 Scala 错误处理的惯用法之前,我们先看看其他语言(如,Java,Ruby)的错误处理机制。 和这些语言类似,Scala 也允许你抛出异常:

case class Customer(age: Int) class Cigarettes case class UnderAgeException(message: String) extends Exception(message) def buyCigarettes(customer: Customer): Cigarettes = if (customer.age < 16) throw UnderAgeException(s"Customer must be older than 16 but was ${customer.age}") else new Cigarettes

被抛出的异常能够以类似 Java 中的方式被捕获,虽然是使用偏函数来指定要处理的异常类型。 此外,Scala 的 try/catch 是表达式(返回一个值),因此下面的代码会返回异常的消息:

val youngCustomer = Customer(15) try { buyCigarettes(youngCustomer) "Yo, here are your cancer sticks! Happy smokin'!" } catch { case UnderAgeException(msg) => msg } 2.2 函数式的错误处理

现在,如果代码中到处是上面的异常处理代码,那它很快就会变得丑陋无比,和函数式程序设计非常不搭。 对于高并发应用来说,这也是一个很差劲的解决方式,比如, 假设需要处理在其他线程执行的 actor 所引发的异常,显然你不能用捕获异常这种处理方式, 你可能会想到其他解决方案,例如去接收一个表示错误情况的消息。

一般来说,在 Scala 中,好的做法是通过从函数里返回一个合适的值来通知人们程序出错了。 别担心,我们不会回到 C 中那种需要使用按约定进行检查的错误编码的错误处理。 相反,Scala 使用一个特定的类型来表示可能会导致异常的计算,这个类型就是 Try。

Try 的语义

解释 Try 最好的方式是将它与 Option 作对比。

Option[A] 是一个可能有值也可能没值的容器, Try[A] 则表示一种计算: 这种计算在成功的情况下,返回类型为 A 的值,在出错的情况下,返回 Throwable 。 这种可以容纳错误的容器可以很轻易的在并发执行的程序之间传递。

Try 有两个子类型:

Success[A]:代表成功的计算。

封装了 Throwable 的 Failure[A]:代表出了错的计算。

如果知道一个计算可能导致错误,我们可以简单的使用 Try[A] 作为函数的返回类型。 这使得出错的可能性变得很明确,而且强制客户端以某种方式处理出错的可能。

假设,需要实现一个简单的网页爬取器:用户能够输入想爬取的网页 URL, 程序就需要去分析 URL 输入,并从中创建一个 java.net.URL :

import scala.util.Try import java.net.URL def parseURL(url: String): Try[URL] = Try(new URL(url))

正如你所看到的,函数返回类型为 Try[URL]: 如果给定的 url 语法正确,这将是 Success[URL], 否则, URL 构造器会引发 MalformedURLException ,从而返回值变成 Failure[URL] 类型。

上例中,我们还用了 Try 伴生对象里的 apply 工厂方法,这个方法接受一个类型为 A 的 传名参数, 这意味着, new URL(url) 是在 Tryapply 方法里执行的。

apply 方法会捕获任何非致命的异常,返回一个包含相关异常的 Failure 实例。

因此, parseURL("") 会返回一个 Success[URL] ,包含了解析后的网址, 而 parseULR("garbage") 将返回一个含有 MalformedURLExceptionFailure[URL]

三. 使用 Try 3.1 初步使用 Try

使用 Try 与使用 Option 非常相似,在这里你看不到太多新的东西。

你可以调用 isSuccess 方法来检查一个 Try 是否成功,然后通过 get 方法获取它的值, 但是,这种方式的使用并不多见,因为你可以用 getOrElse 方法给 Try 提供一个默认值:

val url = parseURL(Console.readLine("URL: ")) getOrElse new URL("http://duckduckgo.com")

如果用户提供的 URL 格式不正确,我们就使用 DuckDuckGo 的 URL 作为备用。

3.2 链式操作

Try 最重要的特征是,它也支持高阶函数,就像 Option 一样。 在下面的示例中,你将看到,在 Try 上也进行链式操作,捕获可能发生的异常,而且代码可读性不错。

Mapping 和 Flat Mapping

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zywpjx.html