Optional 对象是一种包装器对象,要么包装了类型 T 的对象,要么没有包装任何对象。Optional 类型被当作一种更安全的方式,用来替代类型 T 的引用,这种引用要么引用某个对象,要么为 null。

创建 Optional 值

如果想要编写方法来创建 Optional 对象,那么有多个方法可以用于此目的,具体如下:

  1. public static<T> Optional<T> empty()
  2. public static <T> Optional<T> of(T value)
  3. public static <T> Optional<T> ofNullable(T value)

ofNullable() 方法被用来作为可能出现的 null 值和可选值之间的桥梁。of() 方法如果传入的 value 为 null,则会抛出一个 NullPointerException 异常。ofNullable() 方法会在 obj 不为 null 的情况下返回 Optional.of(obj),否则会返回 Optional.empty()。

获取 Optional 值

有效地使用 Optional 的关键是要使用这样的方法:它在值不存在的情况下会产生一个可替代物,而只有在值存在的情况下才会使用这个值。

  1. // 产生这个 Optional 的值,或者在该 Optional 为空时,产生 other
  2. public T orElse(T other)
  3. // 产生这个 Optional 的值,或者在该 Optional 为空时,产生调用 other 的结果
  4. public T orElseGet(Supplier<? extends T> other)
  5. // 产生这个 Optional 的值,或者在该 Optional 为空时,抛出调用 exceptionSupplier 的结果
  6. public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X
  7. // 如果该 Optional 不为空,就将它的值传递给 consumer 进行处理,如果值为空则不做任何处理
  8. public void ifPresent(Consumer<? super T> consumer)

管道化 Optional 值

除了可以从 Optional 对象中获取值,我们还可以保持 Optional 对象的完整,使用 map 等方法来转换 Optional 内部的值。

  1. // 产生一个 Optional,如果当前的 Optional 的值得在,那么所产生的 Optional 的值是通过将给定的函数应用于当前的 Optional 的值而得到的
  2. // 否则,产生一个空的 Optional
  3. public <U> Optional<U> map(Function<? super T, ? extends U> mapper)

这个 map() 方法与 Stream 接口提供的 map() 方法类似。你可以直接将可选值想象成尺寸为 0 或 1 的流。结果的尺寸也是 0 或 1,并且如果流的尺寸为 1 时,mapper 函数会应用于其上。

类似地,可以使用 filter 方法来只处理那些在转换它之前或之后满足某种特定属性的 Optional 值。如果不满足该属性,那么管道会产生空的结果:

  1. // 产生一个Optional,如果当前的 Optional 的值满足给定的谓词条件,那么所产生的Optional 的值就是当前 Optional 的值
  2. // 否则产生一个空 Optional
  3. public Optional<T> filter(Predicate<? super T> predicate)

使用示例:

  1. private List<String> rightMethod(FooService fooService, Integer i) {
  2. log.info("result {} {}", Optional.ofNullable(i).orElse(0) + 1);
  3. Optional.ofNullable(fooService)
  4. .map(FooService::getBarService)
  5. .filter(barService -> "OK".equals(barService.bar()))
  6. .ifPresent(result -> log.info("OK"));
  7. return new ArrayList<>();
  8. }

注意事项

如果没有正确地使用 Optional 值,那么相比普通对象并没有得到任何好处。get() 方法会在 Optional 值存在的情况下获得其中包装的元素,或者在不存在的情况下抛出一个 NoSuchElementException 异常。因此,直接调用 get() 方法获取元素并不比获取普通对象更安全。

下面是一些有关 Optional 类型正确用法的提示:

  • Optional 类型的变量永远都不应该为 null
  • 不要使用 Optional 类型的域。因为其代价是额外多出来一个对象。在类的内部,使用 null 表示缺失的域更易于操作。
  • 不要在集合中放置 Optional 对象,并且不要将它们用作 map 的键,应该直接收集其中的值。