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);
}