Java 新增的Optional方法,用户希望执行以下操作:将 Optional
映射并展平到包含元素的流中;从几种可能的条件中进行选择;当元素存在时进行某种操作,否则返回默认值。使用 Java 9 为 Optional
类新增的 stream
、or
或 ifPresentOrElse
方法。
Java 新增的Optional方法 问题描述
用户希望执行以下操作:将 Optional
映射并展平到包含元素的流中;从几种可能的条件中进行选择;当元素存在时进行某种操作,否则返回默认值。
Java 新增的Optional方法 解决方案
使用 Java 9 为 Optional
类新增的 stream
、or
或 ifPresentOrElse
方法。
Java 新增的Optional方法 具体实例
Java 8 引入了 Optional
类,用于告知客户端返回值可能合法为 null
。返回的并非 null
,而是空 Optional
。对可能返回(或不返回)值的方法而言,Optional
是一种不错的包装器。
1.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<Customer>
,如果尝试返回客户集合则略显复杂。在 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<Customer>>
❷ 筛掉所有空 Optional
❸ 调用 get
方法以映射到 Stream<Customer>
上述实现并不难,但利用 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<Optional<Customer>>
❷ 映射并展平为 Stream<Customer>
使用 stream
纯粹是为了方便,但它不失为一种有用的方法。
2.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<Customer>
,从而将查找客户的其他方式链接在一起。
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
中并返回。
3.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
类新增的这些方法均未从根本上改变各自的用途,但它们确实为开发提供了更大的便利。