Java 8 开始新增的 Optional 类 - Optional 在 Java 8 中的 Chaining 特性

Java 8 Optional 中的 Chaining 特性

有时候我们希望从一系列的 Optional 对象中找到第一个不为 Null 的 Optional 对象。

在上面的情况下,如果我们能够使用 orElseOptional() 方法的话,那就变得非常方便能够实现我们的需求了,但是这个没有办法在 Java 8 直接提供支持。

让我们首先看看有哪些方法我们能够使用来达到上面的目标。

private Optional<String> getEmpty() {
    return Optional.empty();
}

private Optional<String> getHello() {
    return Optional.of("hello");
}

private Optional<String> getBye() {
    return Optional.of("bye");
}

private Optional<String> createOptional(String input) {
    if (input == null || "".equals(input) || "empty".equals(input)) {
        return Optional.empty();
    }
    return Optional.of(input);
}

为了能够在 Java 8 中对一系列的 Optional 进行处理,并且返回第一个不为空的 Optional 对象,我们可以使用 Stream API 来做这件事情。

@Test
public void givenThreeOptionals_whenChaining_thenFirstNonEmptyIsReturned() {
    Optional<String> found = Stream.of(getEmpty(), getHello(), getBye())
      .filter(Optional::isPresent)
      .map(Optional::get)
      .findFirst();
    
    assertEquals(getHello(), found);
}

上面的方法有一个不是非常理想的情况就是不管 Steam 中的 Optional 对象是不是为空,get() 这个方法总是会被执行一次。

为了在 Stream.of() 中进行更有效率的判读,我们需要在 Supplier 接口中使用方法的参考:

@Test
public void givenThreeOptionals_whenChaining_thenFirstNonEmptyIsReturnedAndRestNotEvaluated() {
    Optional<String> found =
      Stream.<Supplier<Optional<String>>>of(this::getEmpty, this::getHello, this::getBye)
        .map(Supplier::get)
        .filter(Optional::isPresent)
        .map(Optional::get)
        .findFirst();

    assertEquals(getHello(), found);
}

在这个用例中,我们需要使用接受参数的方法,同时在 lambda 中使用:

@Test
public void givenTwoOptionalsReturnedByOneArgMethod_whenChaining_thenFirstNonEmptyIsReturned() {
    Optional<String> found = Stream.<Supplier<Optional<String>>>of(
      () -> createOptional("empty"),
      () -> createOptional("hello")
    )
      .map(Supplier::get)
      .filter(Optional::isPresent)
      .map(Optional::get)
      .findFirst();

    assertEquals(createOptional("hello"), found);
}

同时,我们还希望在我们的 Optional 调用中返回一个默认的值,我们还可以添加一个 orElse() 或者 orElseGet() 方法:

@Test
public void givenTwoEmptyOptionals_whenChaining_thenDefaultIsReturned() {
    String found = Stream.<Supplier<Optional<String>>>of(
      () -> createOptional("empty"),
      () -> createOptional("empty")
    )
      .map(Supplier::get)
      .filter(Optional::isPresent)
      .map(Optional::get)
      .findFirst()
      .orElseGet(() -> "default");

    assertEquals("default", found);
}