学习笔记-Optional
2024/2/3大约 4 分钟后端Java
Optional 处理空指针,这几招你会了吗?
作为一个 Java 开发者,谁没被空指针坑过呢?以前我们总得写一堆
if(xxx != null),代码又臭又长。自从 Java 8 出了 Optional,用好了真的能少写很多防御代码。今天就和大家聊聊怎么把这玩意儿用顺手了。
一、What(是什么)
- Optional 是一个用于表示"值可能存在,也可能不存在"的容器类,核心目的是显式处理空值,减少空指针问题。
- 是 Java 8 引入的一个包装类型,用来代替直接返回 null。它通过 map、flatMap、orElse、orElseThrow 等方法,让空值处理更安全、更清晰。
二、How(怎么用)
1. 创建 Optional
ofNullable()并非魔法,它只是将 null 值封装到 Optional 对象中。真正的价值在于后续链式方法带来的安全操作空间。
of 方法(学了没有用)
// 千万别这么写!传 null 直接爆炸
Optional<String> opt = Optional.of(null);
这个 of() 方法就像个暴脾气的老哥,只要你敢传 null,它立马甩你一个空指针异常。只有在 100% 确定对象不为 null 的时候才能用它。(其实没什么用)
最常用的 ofNullable
// 这才是正确打开方式
Optional<User> userOpt = Optional.ofNullable(userService.findById(123));
不管拿到的 user 是不是 null,这个方法都能稳稳接住。相当于给可能为 null 的值套了个保护壳。
2. 兜底默认值/安全取值
兜底取值:orElse
// 有值拿值,没值拿默认
User user = userOpt.orElse(new User("匿名用户"));
适合默认对象创建成本不高的场景。比如这里 new 一个默认用户,内存开销不大。
延迟加载:orElseGet
// 只有需要时才创建默认用户
User user = userOpt.orElseGet(() -> {
// 从数据库加载默认配置
return defaultUserRepository.findByName("guest");
});
抛异常处理
// 找不到就报错
Config config = configOpt.orElseThrow(() -> {
throw new ServiceException("系统配置缺失");
});
把空值转换为业务异常,比直接抛 NPE 有意义多了。比如这里明确告诉调用方是配置缺失的问题。主要是这种书写方式真的太优雅了
3. 链式操作
链式兜底:map 嵌套获取
// 三级安全转换
String city = userOpt
.map(User::getAddress) // 用户 → 地址
.map(Address::getCity) // 地址 → 城市
.orElse("未知地区"); // 保底值
不管哪个环节为 null,都会自动跳转到保底值。相当于给每个 get 方法都加了空判断!
避免套娃:flatMap
// 用户可能有地址,地址可能有坐标
Optional<Coordinate> coord = userOpt
.flatMap(user -> user.getAddress()) // 返回 Optional<Address>
.flatMap(address -> address.getCoord());
- 当你的 get 方法本身就返回 Optional 时,用 flatMap 就不会出现
Optional<Optional<...>>这种套娃结构了 - 但这时候还用
map,结果就会变成这种鬼样子
三、错误用法
1. 不要用 isPresent()+get()
// 错误示范!
if (userOpt.isPresent()) {
User user = userOpt.get();
// 一堆操作
}
这写法跟直接判 null 没区别,完全没发挥 Optional 的优势。应该用 orElse 系列方法替代。
2. 别用在参数和字段上
// 方法参数不要这样写
public void badExample(Optional<String> param) {...}
// 类字段也别用
class User {
private Optional<String> name; // 大忌!
}
Optional 是设计用来做返回值的,滥用反而会让代码变复杂。
3. 空集合别用 Optional
// 返回空集合更直观
public List<Order> findOrders() {
return Collections.emptyList();
}
// 比下面这种写法好
public Optional<List<Order>> findOrders() {...}
- 空集合本身可以很好地表达"没有数据"的含义,无需画蛇添足地用 Optional 包装。
- 🤔 与 JPA 数据库的 API 设计思想一致,JPA 在查询不到数据时,通常返回空集合,而不是 null 或 Optional。
四、实际开发如何使用 Which is best
1. 和 Stream 配合使用
// 过滤掉空值
List<String> names = users.stream()
.map(user -> Optional.ofNullable(user.getName()))
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
不过更推荐这样写:
List<String> names = users.stream()
.map(User::getName)
.filter(Objects::nonNull)
.collect(Collectors.toList());
2. 记录日志更方便
userOpt.ifPresent(user -> {
log.info("用户操作:{}", user.getName());
auditService.record(user);
});
这种写法既安全又直观,不需要先判空再操作。
五、When(什么时候用)
适合的场景:
- 可能有null 的方法返回值
- 需要链式处理可能为 null 的对象时
不适合的场景:
- 集合类(用空集合更好)
- 简单的非空判断(直接 if 判断更直观)
- 性能敏感的场景(Optional 有对象创建开销)
六、省流版
- Optional 不是为了替代一切 null,而是为了显式表达"这里可能为空"。
- 它最适合用在方法返回值,不适合乱用在字段、参数、集合上。
- 最有价值的地方不是
ofNullable,而是后面的链式处理能力。 - 返回普通值用
map,返回 Optional 用flatMap。 - 真正优雅的写法是
orElse、orElseGet、orElseThrow,不是isPresent()+get()。
