它是什么

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 是常量orElsedefault 是函数调用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