Java Optional 那些被忽略的用法

很多人用 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 的判断。

相关推荐
2301_803875613 小时前
PHP 中处理会话数组时的类型错误解析与修复指南
jvm·数据库·python
m0_743623923 小时前
c++如何批量修改文件后缀名_std--filesystem--replace_extension【实战】
jvm·数据库·python
MY_TEUCK3 小时前
Sealos 平台部署实战指南:结合 Cursor 与版本发布流程
java·人工智能·学习·aigc
我爱cope3 小时前
【从0开始学设计模式-10| 装饰模式】
java·开发语言·设计模式
2501_914245934 小时前
CSS如何处理CSS变量作用域冲突_利用特定类名重写变量值
jvm·数据库·python
朝新_4 小时前
【Spring AI 】图像与语音模型实战
java·人工智能·spring
RH2312114 小时前
2026.4.16Linux 管道
java·linux·服务器
zmsofts5 小时前
java面试必问13:MyBatis 一级缓存、二级缓存:从原理到脏数据,一篇讲透
java·面试·mybatis
maqr_1105 小时前
MySQL数据库迁移到云端如何保障安全_数据加密与SSL连接配置
jvm·数据库·python
u0109147605 小时前
MySQL如何限制触发器递归调用的深度_防止触发器死循环方法
jvm·数据库·python