它是什么
Optional<T> = "可能有 T,也可能没有"的容器。Java 8 引入,主要用作"可能为空的返回值":
public Optional<User> findById(int id) {
User u = db.find(id);
return Optional.ofNullable(u);
}
API 签名就告诉调用方"可能没结果"——比返回 null 更明确。
创建
Optional.of("hello"); // 必须非 null(null 抛 NPE)
Optional.ofNullable(maybe); // null 时返 Optional.empty()
Optional.empty(); // 显式空
检查 + 取值
Optional<User> maybe = repo.findById(1);
// 老写法(不推荐)
if (maybe.isPresent()) {
User u = maybe.get();
System.out.println(u);
}
// 推荐:ifPresent
maybe.ifPresent(u -> System.out.println(u));
maybe.ifPresent(System.out::println);
// 取值 + 默认
String name = maybe.map(User::getName).orElse("anonymous");
String name2 = maybe.map(User::getName).orElseGet(() -> "computed " + LocalDate.now());
// 取值 + 异常
User u = maybe.orElseThrow(); // 空时 NoSuchElementException
User u2 = maybe.orElseThrow(() -> new MyException("not found"));
orElse vs orElseGet
maybe.orElse(expensiveDefault()); // ⚠️ 永远计算 default(哪怕 maybe 有值)
maybe.orElseGet(() -> expensiveDefault()); // ✅ 仅在空时计算
default 是常量 → orElse。
default 是函数调用 → orElseGet。
map / filter / flatMap
Optional<String> name = repo.findById(1)
.map(User::getName)
.filter(n -> !n.isBlank())
.map(String::toUpperCase);
// 链式安全:任何环节空就一路传递 empty
flatMap 处理"嵌套 Optional":
public Optional<Address> getAddress(User u) {
return Optional.ofNullable(u.getAddress());
}
Optional<String> city = repo.findById(1)
.flatMap(this::getAddress) // 不 flat 会变 Optional<Optional<Address>>
.map(Address::getCity);
ifPresentOrElse(Java 9+)
maybe.ifPresentOrElse(
u -> System.out.println("found: " + u),
() -> System.out.println("not found")
);
Optional 转 Stream(Java 9+)
List<User> users = ids.stream()
.map(repo::findById) // Stream<Optional<User>>
.flatMap(Optional::stream) // 自动过滤 empty + 展开
.toList();
.stream() 把 Optional<T> 变成 0 元素或 1 元素的流——配合 flatMap 很优雅。
OptionalInt / OptionalLong / OptionalDouble
OptionalInt n = nums.stream().mapToInt(Integer::intValue).max();
int max = n.orElse(0);
避免 Optional<Integer> 的装箱开销。但用法稍尴尬(没有 map 方法)——一般场景 Optional<Integer> 就行。
该用 vs 不该用
✅ 该用:方法返回值
public Optional<User> findById(int id);
public Optional<String> getConfig(String key);
API 契约清晰,调用方必须处理空情况。
❌ 不该用:字段
public class User {
private Optional<String> email; // ❌
}
理由:
- 序列化 / JPA / Jackson 等框架不友好
- 字段可以直接
String email;(可空)+ 暴露Optional<String> getEmail() - 多占一层包装
❌ 不该用:方法参数
public void process(Optional<User> user); // ❌
理由:调用方反正得包一层,不如直接 process(User user) + 调用方判断。
❌ 不该用:集合
Optional<List<User>> users; // ❌
理由:用空列表 List.of() 比 Optional<List<User>>.empty() 干净——空集合天然表"没结果"。
❌ 不该用:到处包
public Optional<String> trim(Optional<String> s) { // ❌
return s.map(String::trim);
}
让调用方先 unwrap 再传。Optional 用在 API 边界,不要内部到处传。
常见反模式
反模式 1:先 isPresent 再 get
// ❌ 啰嗦
if (opt.isPresent()) {
String s = opt.get();
process(s);
}
// ✅ ifPresent
opt.ifPresent(s -> process(s));
// 或要返回值:map/orElse
String r = opt.map(this::process).orElse("default");
反模式 2:用 Optional 替代 null check
// ❌ 不要这样把 null 强行包
Optional<String> s = Optional.ofNullable(possiblyNull);
if (s.isPresent()) { ... }
// ✅ 普通 null check 就够
if (possiblyNull != null) { ... }
Optional 是"在 API 签名上明确表达可能为空"——内部代码用 null + null check 更轻量。
反模式 3:Optional.of(null) 而不是 ofNullable
Optional.of(maybe); // ❌ maybe 为 null 抛 NPE
Optional.ofNullable(maybe); // ✅
反模式 4:orElse 替代 try-catch
Optional<Integer> n = ...;
int x = n.orElse(parseFallback()); // 始终调 parseFallback——可能你不想
// 想要"懒计算"
int y = n.orElseGet(this::parseFallback);
心智模型
Optional<T> 就是"可能为空"的类型化包装。Java 没有 Kotlin / TypeScript 的 T? 语法,Optional 是次优替代。
- 写新 API 的返回值:考虑用 Optional
- 内部代码:null 也行
- 历史代码迁移:从最外层 API 开始包装,慢慢扩散
→ 下一篇 模式匹配 + switch