Optional如何避免空指针异常

wen java案例 2

本文目录导读:

Optional如何避免空指针异常

  1. 核心原则:用 Optional 表示可能缺失的返回值
  2. 避免使用 Optional.of(null)
  3. 利用 Optional 提供的“安全”方法
  4. 不要对 Optional 本身进行判空
  5. 不要将 get() 作为默认选择
  6. 总结:使用 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。

抱歉,评论功能暂时关闭!