类型 Option
前几章,我们讨论了许多相当先进的技术,尤其是模式匹配和提取器。 是时候来看一看 Scala 另一个基本特性了: Option 类型。
可能你已经见过它在 Map
API 中的使用;在实现自己的提取器时,我们也用过它, 然而,它还需要更多的解释。 你可能会想知道它到底解决什么问题,为什么用它来处理缺失值要比其他方法好, 而且可能你还不知道该怎么在你的代码中使用它。 这一章的目的就是消除这些问号,并教授你作为一个新手所应该了解的Option
知识。
1、基本概念
Java 开发者一般都知道 NullPointerException
(其他语言也有类似的东西), 通常这是由于某个方法返回了 null
,但这并不是开发者所希望发生的,代码也不好去处理这种异常。
值 null
通常被滥用来表征一个可能会缺失的值。 不过,某些语言以一种特殊的方法对待 null
值,或者允许你安全的使用可能是 null
的值。 比如说,Groovy 有 安全运算符(Safe Navigation Operator) 用于访问属性, 这样 foo?.bar?.baz
不会在 foo
或 bar
是 null
时而引发异常,而是直接返回 null
, 然而,Groovy 中没有什么机制来强制你使用此运算符,所以如果你忘记使用它,那就完蛋了!
Clojure 对待 nil
基本上就像对待空字符串一样。 也可以把它当作列表或者映射表一样去访问,这意味着,nil
在调用层级中向上冒泡。 很多时候这样是可行的,但有时会导致异常出现在更高的调用层级中,而那里的代码没有对 nil
加以考虑。
Scala 试图通过摆脱 null
来解决这个问题,并提供自己的类型用来表示一个值是可选的(有值或无值), 这就是 Option[A]
特质。
Option[A]
是一个类型为 A
的可选值的容器: 如果值存在, Option[A]
就是一个 Some[A]
,如果不存在, Option[A]
就是对象 None
。
在类型层面上指出一个值是否存在,使用你的代码的开发者(也包括你自己)就会被编译器强制去处理这种可能性, 而不能依赖值存在的偶然性。
Option
是强制的!不要使用 null
来表示一个值是缺失的。
2、创建 Option
通常,你可以直接实例化 Some
样例类来创建一个 Option 。
val greeting: Option[String] = Some("Hello world")
或者,在知道值缺失的情况下,直接使用 None
对象:
val greeting: Option[String] = None
然而,在实际工作中,你不可避免的要去操作一些 Java 库, 或者是其他将 null
作为缺失值的JVM 语言的代码。 为此, Option
伴生对象提供了一个工厂方法,可以根据给定的参数创建相应的 Option
:
val absentGreeting: Option[String] = Option(null) // absentGreeting will be None
val presentGreeting: Option[String] = Option("Hello!") // presentGreeting will be Some("Hello!")
3、使用 Option
目前为止,所有的这些都很简洁,不过该怎么使用 Option 呢?是时候开始举些无聊的例子了。
想象一下,你正在为某个创业公司工作,要做的第一件事情就是实现一个用户的存储库, 要求能够通过唯一的用户 ID 来查找他们。 有时候请求会带来假的 ID,这种情况,查找方法就需要返回 Option[User]
类型的数据。 一个假想的实现可能是:
case class User(
id: Int,
firstName: String,
lastName: String,
age: Int,
gender: Option[