10.6 新增的Optional方法
问题
用户希望执行以下操作:将 Optional 映射并展平到包含元素的流中;从几种可能的条件中进行选择;当元素存在时进行某种操作,否则返回默认值。
方案
使用 Java 9 为 Optional 类新增的 stream、or 或 ifPresentOrElse 方法。
讨论
Java 8 引入了 Optional 类,用于告知客户端返回值可能合法为 null。返回的并非 null,而是空 Optional。对可能返回(或不返回)值的方法而言,Optional 是一种不错的包装器。
stream方法
例 10-28 显示了一种根据 ID 检索客户的查找器方法(finder method)。
例 10-28 根据 ID 查找客户
- public Optional<Customer> findById(int id) {
- return Optional.ofNullable(map.get(id));
- }
findById 方法假设客户包含在内存的 Map 中。map.get 方法在键存在时返回一个值,否则返回 null,因此将其作为 Optional.ofNullable 方法的参数时,要么在 Optional 中包装一个非空值,要么返回一个空 Optional。
由于
Optional.of方法在参数为null时将抛出异常,采用Optional.ofNullable(arg)更方便,其实现为arg != null ? Optional.of(arg) : Optional.empty()。
findById 方法返回的是 Optional,如果尝试返回客户集合则略显复杂。在 Java 8 中,不难写出如例 10-29 所示的代码。
例 10-29 在
Optional上使用filter和map方法
- public Collection<Customer> findAllById(Integer... ids) {
- return Arrays.stream(ids)
- .map(this::findById) ➊
- .filter(Optional::isPresent) ➋
- .map(Optional::get) ➌
- .collect(Collectors.toList());
- }
❶ 映射到 Stream
❷ 筛掉所有空 Optional
❸ 调用 get 方法以映射到 Stream
上述实现并不难,但利用 Java 9 为 Optional 类新增的 stream 方法,可以使这个过程更简单。stream 方法的签名如下:
Stream<T> stream()
如果值存在,stream 方法将返回一个顺序的单元素流,流中仅包含该值;如果值不存在,stream 方法将返回一个空流。借由 stream 方法,可选的客户流可以直接转换为客户流,如例 10-30 所示。
例 10-30 使用传入
Optional.stream的flatMap方法
- public Collection<Customer> findAllById(Integer... ids) {
- return Arrays.stream(ids)
- .map(this::findById) ➊
- .flatMap(Optional::stream) ➋
- .collect(Collectors.toList());
- }
❶ 映射到 Stream
❷ 映射并展平为 Stream
使用 stream 纯粹是为了方便,但它不失为一种有用的方法。
or方法
orElse 方法用于从 Optional 中提取值,它传入默认值作为参数:
Customer customer = findById(id).orElse(Customer.DEFAULT)
也可以使用传入 Supplier 来创建默认值的 orElseGet 方法,不过这是一种成本较高的操作:
Customer customer = findById(id).orElseGet(() -> createDefaultCustomer())
orElse 和 orElseGet 方法均返回 Customer 实例。而 Java 9 新增的 or 方法可以在给定 Supplier 时返回 Optional,从而将查找客户的其他方式链接在一起。
or 方法的签名如下:
- Optional<T> or(Supplier<? extends Optional<? extends T>> supplier)
如果值存在,or 方法将返回描述该值的 Optional,否则返回由 Supplier 生成的 Optional。
我们现在可以通过多种方式查找客户,例 10-31 展示了 or 方法的应用。
例 10-31 改用
or方法
- public Optional<Customer> findById(int id) {
- return findByIdLocal(id)
- .or(() -> findByIdRemote(id))
- .or(() -> Optional.of(Customer.DEFAULT));
- }
程序首先在本地缓存中搜索客户,再访问远程服务器。如果两种方式都未能找到非空 Optional,最后一个子句创建一个默认值,将其包装在 Optional 中并返回。
ifPresentOrElse方法
如果 Optional 不为空,ifPresent 方法将执行 Consumer 指定的操作,如例 10-32 所示。
例 10-32 利用
ifPresent方法仅打印非空客户
- public void printCustomer(Integer id) {
- findByIdLocal(id).ifPresent(System.out::println); ➊
- }
- public void printCustomers(Integer... ids) {
- Arrays.asList(ids)
- .forEach(this::printCustomer);
- }
➊ 仅打印非空 Optional
虽然 ifPresent 方法很有用,不过如果返回的 Optional 为空,我们可能还希望执行其他操作。Java 9 新增的 ifPresentOrElse 方法包括两个参数,在 Optional 为空时将执行第二个参数 Runnable 指定的操作。该方法的签名如下:
- void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction)
使用 ifPresentOrElse 方法时,只需提供一个不传入任何参数并返回 void 的 lambda 表达式,如例 10-33 所示。
例 10-33 打印客户或默认消息
- public void printCustomer(Integer id) {
- findByIdLocal(id).ifPresentOrElse(System.out::println,
- () -> System.out.println("Customer with id=" + id + " not found"));
- }
如果找到指定的客户,程序将打印该客户,否则打印默认消息。
Optional 类新增的这些方法均未从根本上改变各自的用途,但它们确实为开发提供了更大的便利。
另见
有关 Java 8 引入的 Optional 类请参见第 6 章。
由于 