Java 从Optional中检索值

Java 从Optional中检索值,用户希望从 Optional 中提取包含的值,如果确定 Optional 中存在值,则使用 get 方法,否则使用 orElse 方法的某种形式。如果只希望当值存在时才执行 Consumer,也可以使用 ifPresent 方法。

Java 从Optional中检索值 问题描述

用户希望从 Optional 中提取包含的值。

Java 从Optional中检索值 解决方案

如果确定 Optional 中存在值,则使用 get 方法,否则使用 orElse 方法的某种形式。如果只希望当值存在时才执行 Consumer,也可以使用 ifPresent 方法。

Java 从Optional中检索值 具体实例

当调用一个返回 Optional 的方法时,可以通过调用 get 方法来检索其中包含的值。如果 Optional 为空,get 方法将抛出 NoSuchElementException
接下来,我们讨论如何从一个字符串流中返回第一个偶数长度的字符串,如例 6-4 所示。

例 6-4 检索第一个偶数长度的字符串

Optional<String> firstEven =
    Stream.of("five", "even", "length", "string", "values")
        .filter(s -> s.length() % 2 == 0)
        .findFirst();

由于流中的所有字符串都无法通过筛选器,findFirst 方法将返回 Optional<String>。可以通过在 Optional 上调用 get 方法来打印返回值:

System.out.println(firstEven.get()) // 即便这条语句可以执行,也应避免使用

请注意,虽然上述语句可以执行,但除非确定 Optional 中包含值,否则不要调用 get 方法,以免程序抛出异常,如例 6-5 所示。

例 6-5 检索第一个奇数长度的字符串

Optional<String> firstOdd =
    Stream.of("five", "even", "length", "string", "values")
        .filter(s -> s.length() % 2 != 0)
        .findFirst();

System.out.println(firstOdd.get()); // 抛出NoSuchElementException

我们可以采用多种方案解决这个问题。例如,首先确定 Optional 中是否包含值,然后再检索,如例 6-6 所示。

例 6-6 检索第一个偶数长度的字符串(先确定是否包含值,再使用 get 方法)

Optional<String> firstEven =                                  ➊
    Stream.of("five", "even", "length", "string", "values")
          .filter(s -> s.length() % 2 == 0)
          .findFirst();

System.out.println(
    first.isPresent() ? first.get() : "No even length strings");  ➋

❶ 与例 6-4 所示的代码相同
❷ 仅当 isPresent 方法返回 true 时才调用 get 方法
不过,虽然上述代码可以执行,但只是添加了一个 isPresent 方法进行非空验证,程序并未有太大改进。
所幸我们还有一个更好的选择,这就是非常方便的 orElse 方法,如例 6-7 所示。

例 6-7 使用 orElse 方法

Optional<String> firstOdd =
    Stream.of("five", "even", "length", "string", "values")
        .filter(s -> s.length() % 2 != 0)
        .findFirst();

System.out.println(firstOdd.orElse("No odd length strings"));

如果包含的值存在,则 orElse 方法返回该值,否则返回提供的默认值。因此,如果已有回退值(fallback value),那么采用 orElse 方法是相当方便的。
orElse 方法包括三种形式。

  • 当包含的值存在时,orElse(T other) 将返回该值,否则返回指定的默认值 other
  • 当包含的值存在时,orElseGet(Supplier<? extends T> other) 将返回该值,否则调用 Supplier 并返回相应的结果。
  • 当包含的值存在时,orElseThrow(Supplier<? extends X> exceptionSupplier) 将返回该值,否则抛出由 Supplier 产生的异常。

orElseorElseGet 方法的不同之处在于,无论 Optional 中是否存在值,orElse 总会创建新字符串;而 orElseGet 使用 Supplier,仅在 Optional 为空时才执行。
如果值只是一个简单的字符串,那么 orElseorElseGet 方法的区别可以忽略不计;如果 orElse 方法的参数是一个复杂对象,那么传入 SupplierorElseGet 方法能确保仅在需要时才创建对象。相关示例如例 6-8 所示。

例 6-8 在 orElseGet 方法中使用 Supplier

Optional<ComplexObject> val = values.stream.findFirst()

val.orElse(new ComplexObject());         ➊
val.orElseGet(() -> new ComplexObject()) ➋

❶ 总是创建新对象
❷ 仅在需要时才创建对象

采用 Supplier 作为方法参数是延迟执行(deferred execution)或惰性执行(lazy execution)的一种应用,从而可以仅在需要时才在 Supplier 上调用 get 方法。3

3参见 Venkat Subramaniam 撰写的《Groovy 程序设计》一书,Optional的创建详细讨论了这个问题。

Java 标准库中,orElseGet 方法的实现如例 6-9 所示。

例 6-9 orElseGet 方法的实现

public T orElseGet(Supplier<? extends T> other) {
    return value != null ? value : other.get(); ➊
}

valueOptionalT 类型的最终特性
orElseThrow 方法同样传入 Supplier,该方法的签名如下:

<X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier)

因此,当 Optional 包含值时,不会执行用作 Supplier 参数的构造函数引用,如例 6-10 所示。

例 6-10 采用 orElseThrow 作为 Supplier

Optional<String> first =
    Stream.of("five", "even", "length", "string", "values")
        .filter(s -> s.length() % 2 == 0)
        .findFirst();

System.out.println(first.orElseThrow(NoSuchElementException::new));

此外,ifPresent 方法支持提供一个仅当 Optional 包含值时才执行的 Consumer,如例 6-11 所示。

例 6-11 ifPresent 方法的应用

Optional<String> first =
    Stream.of("five", "even", "length", "string", "values")
        .filter(s -> s.length() % 2 == 0)
        .findFirst();

first.ifPresent(val -> System.out.println("Found an even-length string"));

first = Stream.of("five", "even", "length", "string", "values")
    .filter(s -> s.length() % 2 != 0)
    .findFirst();

first.ifPresent(val -> System.out.println("Found an odd-length string"));

执行上述程序将仅打印“Found an even-length string”消息。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程