很多人用 Optional 就是 ifPresent,稍微好一点会用 orElse。但 Optional 其实有不少好用的方法,一直没被充分利用。
一、orElse vs orElseGet 别搞混
这两个都会在 Optional 为空时返回默认值,但有个关键区别:
java
// orElse:无论是否为空,orElse 的参数都会执行
String a = Optional.ofNullable(getName()).orElse(getDefaultName());
// 等同于:getName() ?? getDefaultName()
// 不管 getName() 返回什么,getDefaultName() 一定会被调用
// orElseGet:只有为空时才执行
String b = Optional.ofNullable(getName()).orElseGet(() -> getDefaultName());
// 只有 getName() 为空时,getDefaultName() 才会被调用
如果 getDefaultName() 是从数据库查或者调用接口,这个差别就大了。日常开发里 orElseGet 性能更好,但容易被忽略。
二、filter 过滤不要的条件
这个组合很多人不知道:
java
// 传统写法
if (user != null && user.getAge() >= 18) {
doSomething(user);
}
// Optional 写法
Optional.ofNullable(user)
.filter(u -> u.getAge() >= 18)
.ifPresent(this::doSomething);
filter 会把不满足条件的 Optional 变成空,后面的操作自然跳过。
三、map 链式取值,告别层层判空
这应该是 Optional 最核心的用法:
java
// 传统写法:每一层都可能为空
String city = null;
if (user != null) {
if (user.getAddress() != null) {
city = user.getAddress().getCity();
}
}
// Optional 写法:一行搞定
String city = Optional.ofNullable(user)
.map(User::getAddress)
.map(Address::getCity)
.orElse("未知");
每一层 map 遇到空值就中断,最后返回空 Optional,不会抛 NPE。
四、flatMap 处理列表
java
// 从用户里取订单,再从订单里取商品
// 传统写法
if (user != null && user.getOrders() != null && !user.getOrders().isEmpty()) {
Product product = user.getOrders().get(0).getProduct();
}
// flatMap 写法
Optional.ofNullable(user)
.flatMap(u -> u.getOrders().stream().findFirst())
.map(Order::getProduct);
flatMap 的特点是返回的还是 Optional,适合用在需要二次转换的场景。
五、orElseThrow 有预期地抛异常
当值为空时,不给默认值,而是抛自定义异常:
java
User user = Optional.ofNullable(userId)
.map(this::findById)
.orElseThrow(() -> new BizException("用户不存在"));
比 orElse(null) 强多了,至少是个明确的异常而不是后面的 NPE。
六、ifPresentOrElse 兼顾两种情况
JDK 9 引入的,有值和没值分别处理:
java
Optional.ofNullable(user)
.ifPresentOrElse(
u -> System.out.println("欢迎 " + u.getName()),
() -> System.out.println("用户不存在")
);
以前要写 if-else,现在一行搞定。
七、Optional 不是银弹
Optional 适合用在方法返回值 和链式处理上,但别滥用:
java
// 不建议:成员变量用 Optional
private Optional<String> name; // 序列化麻烦
// 不建议:参数用 Optional
void getUser(Optional<Long> userId); // 调用方还得包装
// 不建议:集合用 Optional
private Optional<List<String>> names; // 空集合比 Optional 更合理
Optional 的定位是库方法返回值,避免调用方 NPE,不是用来改造整个代码结构的。
总结
| 方法 | 用途 | 容易被忽略程度 |
|---|---|---|
orElseGet |
延迟计算默认值 | ⭐⭐⭐ |
filter |
过滤不要的值 | ⭐⭐⭐ |
flatMap |
处理列表/二次转换 | ⭐⭐⭐⭐ |
orElseThrow |
空时抛明确异常 | ⭐⭐⭐ |
ifPresentOrElse |
兼顾有无两种情况 | ⭐⭐ |
用好 Optional 能让代码整洁很多,至少不用写一堆 != null 的判断。