第 11 章 用Optional取代null

本章内容

  • null引用引发的问题,以及为什么要避免null引用
  • nullOptional:以null安全的方式重写你的域模型
  • Optional发光发热: 去除代码中对null的检查
  • 读取Optional中可能值的几种方法
  • 对可能缺失值的再思考

如果你作为Java程序员曾经遭遇过NullPointerException,请举起手。如果这是你最常遭遇的异常,请继续举手。非常可惜,这个时刻,我们无法看到对方,但是我相信很多人的手这个时刻是举着的。我们还猜想你可能也有这样的想法:“毫无疑问,我承认,对任何一位Java程序员来说,无论是初出茅庐的新人,还是久经江湖的专家,NullPointerException都是他心中的痛,可是我们又无能为力,因为这就是我们为了使用方便甚至不可避免的像null引用这样的构造所付出的代价。”这就是程序设计世界里大家都持有的观点,然而,这可能并非事实的全部真相,只是我们根深蒂固的一种偏见。

1965年,英国一位名为Tony Hoare的计算机科学家在设计ALGOL W语言时提出了null引用的想法。ALGOL W是第一批在堆上分配记录的类型语言之一。Hoare选择null引用这种方式,“只是因为这种方法实现起来非常容易”。虽然他的设计初衷就是要“通过编译器的自动检测机制,确保所有使用引用的地方都是绝对安全的”,他还是决定为null引用开个绿灯,因为他认为这是为不存在的值建模最容易的方式。很多年后,他开始为自己曾经做过这样的决定而后悔不迭,把它称为“我价值百万的重大失误”。我们已经看到它带来的后果——程序员对对象的字段进行检查,判断它的值是否为期望的格式,最终却发现查看的并不是一个对象,而是一个空指针,它会立即抛出一个让人厌烦的NullPointerException异常。

实际上,Hoare低估了过去五十年来数百万程序员为修复null引用所耗费的代价。近十年出现的大多数现代程序设计语言1,包括Java,都采用了同样的设计方式,其原因是为了与更老的语言保持兼容,或者就像Hoare曾经陈述的那样,“仅仅是因为这样实现起来更加容易”。让我们从一个简单的例子入手,看看使用null都有什么样的问题。

1为数不多的几个最著名的例外是典型的函数式语言,比如Haskell、ML;这些语言中引入了代数数据类型,允许显式地声明数据类型,明确地定义了特殊变量值(比如null)能否使用在定义类型的类型(type-by-type basis)中。