4.10 Optional

reduce方法的一个重点尚未提及:reduce方法有两种形式,一种如前面出现的需要有一个初始值,另一种变式则不需要有初始值。没有初始值的情况下,reduce的第一步使用Stream中的前两个元素。有时,reduce操作不存在有意义的初始值,这样做就是有意义的,此时,reduce方法返回一个Optional对象。

Optional是为核心类库新设计的一个数据类型,用来替换null值。人们对原有的null值有很多抱怨,甚至连发明这一概念的Tony Hoare也是如此,他曾说这是自己的一个“价值连城的错误”。作为一名有影响力的计算机科学家就是这样:虽然连一毛钱也见不到,却也可以犯一个“价值连城的错误”。

人们常常使用null值表示值不存在,Optional对象能更好地表达这个概念。使用null代表值不存在的最大问题在于NullPointerException。一旦引用一个存储null值的变量,程序会立即崩溃。使用Optional对象有两个目的:首先,Optional对象鼓励程序员适时检查变量是否为空,以避免代码缺陷;其次,它将一个类的API中可能为空的值文档化,这比阅读实现代码要简单很多。

下面我们举例说明Optional对象的API,从而切身体会一下它的使用方法。使用工厂方法of,可以从某个值创建出一个Optional对象。Optional对象相当于值的容器,而该值可以通过get方法提取。如例4-22所示。

例4-22 创建某个值的Optional对象

  1. Optional<String> a = Optional.of("a");
  2. assertEquals("a", a.get());

Optional对象也可能为空,因此还有一个对应的工厂方法empty,另外一个工厂方法ofNullable则可将一个空值转换成Optional对象。例4-23展示了这两个方法,同时展示了第三个方法isPresent的用法(该方法表示一个Optional对象里是否有值)。

例4-23 创建一个空的Optional对象,并检查其是否有值

  1. Optional emptyOptional = Optional.empty();
  2. Optional alsoEmpty = Optional.ofNullable(null);
  3. assertFalse(emptyOptional.isPresent());
  4. // 例4-22中定义了变量a
  5. assertTrue(a.isPresent());

使用Optional对象的方式之一是在调用get()方法前,先使用isPresent检查Optional对象是否有值。使用orElse方法则更简洁,当Optional对象为空时,该方法提供了一个备选值。如果计算备选值在计算上太过繁琐,即可使用orElseGet方法。该方法接受一个Supplier对象,只有在Optional对象真正为空时才会调用。例4-24展示了这两个方法。

例4-24 使用orElseorElseGet方法

  1. assertEquals("b", emptyOptional.orElse("b"));
  2. assertEquals("c", emptyOptional.orElseGet(() -> "c"));

Optional对象不仅可以用于新的Java 8 API,也可用于具体领域类中,和普通的类别无二致。当试图避免空值相关的缺陷,如未捕获的异常时,可以考虑一下是否可使用Optional对象。