334. Java Stream API - 正确使用 Java Optional 的七大黄金法则 + 消费方法讲解
🔍 如何消费 Optional 的内容
Optional 提供了两个很实用的方法,可以“消费”它内部的值(如果有):
✅ ifPresent(Consumer<T>)
这个方法的语义是:“如果有值,就处理它”。就像 Stream.forEach() 的缩小版。
📌 示例:
Optional<String> optional = Optional.of("Hello");
optional.ifPresent(value -> System.out.println("Value is: " + value));
输出:
Value is: Hello
如果 optional 是空的,啥也不发生。适合做 “可有可无的值的处理”。
✅ ifPresentOrElse(Consumer<T>, Runnable)
Java 9 加入的新方法。
📌 语义是:
- 如果有值 → 执行第一个参数(
Consumer) - 如果没值 → 执行第二个参数(
Runnable)
📌 示例:
Optional<String> optional = Optional.empty();
optional.ifPresentOrElse(
value -> System.out.println("Value is: " + value),
() -> System.out.println("No value present!")
);
输出:
No value present!
🚀 用于在一个地方处理“有”和“无”的两种情况,更清晰更优雅。
🎯 七条 Optional 正确使用的黄金法则
✅ 规则 #1:绝对不要用 null 表示 Optional 没有值
错误做法 ❌:
Optional<String> optional = null; // 千万别这样!
正确做法 ✅:
Optional<String> optional = Optional.empty(); // 空的 optional
🔒 理由:Optional 的存在就是为了避免 null。如果你用 null 表示 Optional,反而制造了更大的风险!
✅ 规则 #2:除非你非常确定,别用 get() 或 orElseThrow()
错误用法(风险巨大)❌:
String value = optional.get(); // 如果是 empty,就抛异常
更安全的做法 ✅:
optional.ifPresent(val -> System.out.println("Found: " + val));
或者配合 orElse()、orElseGet():
String value = optional.orElse("default");
⚠️ 仅在你百分之百确信 Optional 一定有值的地方,才使用 get() 或 orElseThrow()。
✅ 规则 #3:尽量避免用 ifPresent() / get() / orElseThrow() 作流程控制
👎 不推荐的 ifPresent + else 模式:
if (optional.isPresent()) {
doSomething(optional.get());
} else {
doSomethingElse();
}
👍 更推荐:
optional.ifPresentOrElse(
this::doSomething,
this::doSomethingElse
);
更函数式、更现代化、更清晰。
✅ 规则 #4:不要用 Optional 来替代 null 判断
错误使用 ❌:
Optional<String> optional = Optional.ofNullable(name);
if (optional.isPresent()) {
// do something
}
更直接做法 ✅:
if (name != null) {
// do something
}
📌 Optional 是为了作为方法返回值的契约,不是为了你写 if 判断时图省事。
✅ 规则 #5:不要在以下位置使用 Optional:
| ❌ 错误用法 | 原因 |
|---|---|
Optional 作为字段 |
不序列化,且使用成本高 |
Optional 作为方法参数 |
调用者很不方便传入 |
Optional 放在集合中 |
组合复杂度高,性能低 |
Optional 做为 Map 值 |
造成空值和逻辑判断的双重复杂度 |
正确做法 ✅:
- 方法参数用
@Nullable或javadoc说明 - 字段用普通引用 + 文档约定
✅ 规则 #6:不要对 Optional 做身份比较或同步操作
错误示例 ❌:
if (optional1 == optional2) { ... } // 引用比较没有意义
更正确做法 ✅:
if (optional1.equals(optional2)) { ... }
同步也是危险的:
synchronized(optional) { ... } // 千万别这么写
原因:Optional 是值对象,不该参与同步、锁等“身份敏感操作”。
✅ 规则 #7:**Optional不是可序列化的对象**
在 Java 的设计中,Optional 明确不是 Serializable。因此不能用于:
- 网络传输对象
- 存储结构
-
JSON序列化
替代方法 ✅:
- 使用
null +文档说明 - 自定义 Optional 类型并实现
Serializable(仅在必要场景下)
🧠 总结口诀(便于记忆)
🌱 可选非空不用 null,
🔐 拿值之前先判断,
✋ 少用 get,多用 map,
🧹 不用 Optional 做成员,
🚫 不要同步或比身份。