本文目录导读:

- 核心原则:用
Optional表示可能缺失的返回值 - 避免使用
Optional.of(null) - 利用
Optional提供的“安全”方法 - 不要对
Optional本身进行判空 - 不要将
get()作为默认选择 - 总结:使用
Optional的「避坑清单」
Optional 是 Java 8 引入的一个容器类,用于更好地处理可能为 null 的值,它的核心目的不是完全消灭 NullPointerException(NPE),而是通过显式地要求开发者处理空值情况,从而降低 NPE 发生的概率。
如果不正确使用,Optional 本身也可能导致 NPE(Optional.of(null)),下面详细说明如何正确使用 Optional 来避免 NPE。
核心原则:用 Optional 表示可能缺失的返回值
- 应该做:在方法的返回值类型中使用
Optional,明确告知调用者:“这个方法可能没有结果,请处理这种情况。” - 不应该做:
- 将
Optional用作字段(会使类序列化复杂,且不是设计初衷)。 - 将
Optional用作方法参数(会把“处理空值”的责任推给调用方,不如用重载或@Nullable注解)。
- 将
示例(正确用法):
// 返回可能为空的用户
public Optional<User> findUserById(String id) {
// 假设数据库查询
User user = userRepository.findById(id);
// 正确:用 Optional 包装,明确可能为空
return Optional.ofNullable(user);
}
避免使用 Optional.of(null)
这是最容易触发 NPE 的地方。
Optional.of(value):要求 value 绝对不能为 null,如果传入 null,会立即抛出 NPE。Optional.ofNullable(value):value 可以为 null,如果为 null 则返回Optional.empty()。
正确做法:
// ❌ 危险:user 是 null,这里就抛出 NPE 了
public Optional<User> findUserBad(String id) {
User user = userRepository.findById(id);
return Optional.of(user);
}
// ✅ 安全:user 是 null,返回 Optional.empty()
public Optional<User> findUserGood(String id) {
User user = userRepository.findById(id);
return Optional.ofNullable(user);
}
利用 Optional 提供的“安全”方法
Optional 提供了一系列方法,让你不再需要显式写 if-else 判断 null,从而减少 NPE 风险。
1 orElse(T other)
Optional 有值则返回该值,否则返回 other。
// 传统写法(容易忘记判空)
String name = user.getName();
String displayName = (name != null) ? name : "Unknown";
// 使用 Optional
String displayName = Optional.ofNullable(user)
.map(User::getName)
.orElse("Unknown"); // 绝不可能返回 null
2 orElseGet(Supplier<? extends T> other)
类似于 orElse,但 other 是一个惰性求值的供应函数,如果创建默认值开销大时,使用这个方法。
// orElse:即使有值,也会创建默认对象(性能浪费)
User defaultUser = new User("default");
User result = optionalUser.orElse(defaultUser);
// orElseGet:只有值为空时才创建默认对象
User result = optionalUser.orElseGet(() -> new User("default"));
3 orElseThrow(Supplier<? extends X> exceptionSupplier)
如果值为空,则抛出指定的异常,常用于不允许值为空的场景。
User user = optionalUser.orElseThrow(() -> new IllegalArgumentException("User not found"));
// 这里 user 绝对不为 null,后续使用无需判空
sysout(user.getName()); // 安全
4 ifPresent(Consumer<? super T> consumer)
如果值存在,则调用 consumer;否则什么也不做,避免显式 if (x != null)。
// 传统写法
if (user != null && user.getEmail() != null) {
sendEmail(user.getEmail());
}
// 使用 Optional
Optional.ofNullable(user)
.map(User::getEmail)
.ifPresent(this::sendEmail); // 安全链式调用
5 filter(Predicate<? super T> predicate)
安全地过滤值,如果值满足条件则返回该 Optional,否则返回 Optional.empty()。
// 传统写法:需要双重判空
if (user != null && user.getAge() >= 18) {
processAdult(user);
}
// 使用 Optional
Optional.ofNullable(user)
.filter(u -> u.getAge() >= 18)
.ifPresent(this::processAdult); // 安全
6 map(Function<? super T, ? extends U> mapper)
安全地转换值,如果值为空,则直接返回 Optional.empty(),不会调用 mapper。这是避免 NPE 最重要的方法。
// 传统写法:容易写出 NPE
String city = user.getAddress().getCity(); // user 或 address 为 null,则 NPE
// 使用 Optional:链式调用安全
String city = Optional.ofNullable(user)
.map(User::getAddress) // user 为 null,返回 empty,不继续执行
.map(Address::getCity) // address 为 null,返回 empty,不继续执行
.orElse("Unknown"); // 提供默认值,绝无 NPE
不要对 Optional 本身进行判空
你已经声明了一个 Optional 变量,就不应该再检查它是否为 null。Optional 本身不应该为 null。
// ❌ 错误:既然用了 Optional,就不应该再检查 null
public void process(Optional<User> userOpt) {
if (userOpt == null) {
// 这说明调用方没按约定传 Optional,而是传了 null
}
}
// ✅ 正确:直接使用 Optional 的方法
public void process(Optional<User> userOpt) {
// 即使 userOpt 为 null,这里也会 NPE,所以调用方必须确保参数不为 null。
userOpt.ifPresent(u -> sysout(u.getName()));
}
最佳实践:如果参数可能为 null,不要用 Optional 作为参数类型,用 @Nullable 注解更合适。
不要将 get() 作为默认选择
Optional.get() 如果值为空,会抛出 NoSuchElementException(是 RuntimeException)。除非你百分百确定值存在,否则不要用 get()。
- 需要值:用
orElse()、orElseGet()。 - 需要异常:用
orElseThrow()。 - 不需要值:用
ifPresent()。
使用 Optional 的「避坑清单」
| 场景 | ✅ 推荐做法 | ❌ 危险做法 |
|---|---|---|
| 返回值 | Optional.ofNullable(result) |
返回 null 或 Optional.of(result)(结果可能为 null 时) |
| 构造 Optional | Optional.ofNullable(value) |
Optional.of(value) value 可能为 null |
| 取值 | orElse("default")、orElseGet(Supplier) |
get() (除非确定非空) |
| 转换 | map(User::getName) |
手写 if (user != null) { ... } |
| 条件执行 | filter(...).ifPresent(...) |
手写 if (x != null && condition) {...} |
| 方法参数 | 不推荐使用 Optional改用 @Nullable + 文档 |
使用 Optional 作为参数,并期待调用方传非 null |
一句话总结: Optional 的价值在于通过类型系统强制你考虑空值情况,而不是帮你自动判空,只要你遵循 ofNullable 构造、map/orElse/ifPresent 消费,就能有效避免 NPE。