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 的判断。

相关推荐
Zane199429 分钟前
HashMap 为什么要在长度16、容量必须是2的幂这些细节上较劲
java·后端
长谷深风11143 分钟前
为什么你的 Tool 总被模型选错?
java·大数据·ai·llm·ai agent·工具设计·agent设计
l1258651 小时前
# RAG噪声知识库治理:一致性与可信度的四层防线设计
数据库·人工智能·python·自然语言处理·langchain
莫得感情 o2 小时前
并发 14 · 收官:虚拟线程与结构化并发
java·并发
聚美智数2 小时前
图片水印-图片剪裁-图片缩放API接口介绍
java·服务器·数据库
JavaPub-rodert2 小时前
Go 后台如何同时兼容 MySQL、PostgreSQL、SQLite 和 SQL Server?从 ShiyuAdmin 看 GORM 多数据库适配
数据库·mysql·postgresql·golang·javapub·王仕宇
瀚高PG实验室3 小时前
SQL优化案例:存储过程、复杂SQL拆成简单SQL提升整体性能
数据库·sql·postgresql·瀚高数据库
Wang's Blog3 小时前
PostgreSQL笔记36:执行计划基础解读与优化器成本模型
数据库·笔记·postgresql
xierui1231233 小时前
AI Agent 隐私架构:本地化重点为什么是登录态与执行权限
java·人工智能·网络安全·架构
Wang's Blog3 小时前
PostgreSQL笔记35:索引常见问题诊断与解决方案全景解析
数据库·笔记·postgresql