Optional类

本文最后更新于:3 年前

引言

在 Java 8 之前,null 值一直是造成程序崩溃(NullPointerException)的“头号公敌”。为了解决空指针带来的隐患,Java 8 引入了 java.util.Optional<T> 类型,用于在类型层面上显式表示“值可能缺失”的语义。本文将从原理、常用方法及示例代码等多个维度,系统地介绍 Optional 的使用和最佳实践。

主要作用

  1. 显式表达可选性:传统做法是通过返回 null 或文档约定,告诉调用方“该值可能为空”,但这依赖于调用方手动检查,容易遗漏。
  2. 减少 NullPointerException:Optional 提供了一系列链式 API,使得在访问可能为空的值时,可以优雅地进行处理,避免直接调用导致 NPE。
  3. 提升代码可读性:方法签名返回 Optional<T>,调用者一眼就能看出该值可能不存在,强制其在编译期考虑缺失情况。

基本用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class OptionalBasics {
public static void main(String[] args) {
// 创建非空 Optional,若传入 null 会抛出 NullPointerException
Optional<String> opt1 = Optional.of("Hello");

// 创建可空 Optional,允许传入 null
Optional<String> opt2 = Optional.ofNullable(null);

// 创建空 Optional
Optional<String> opt3 = Optional.empty();

System.out.println(opt1.isPresent()); // true
System.out.println(opt2.isPresent()); // false
System.out.println(opt3.isPresent()); // false
}
}

常用方法详解

方法 用途
isPresent() 判断是否包含值
get() 获取值,若为空则抛出 NoSuchElementException
ifPresent(Consumer) 值存在时执行回调
orElse(T other) 值为空时返回 other
orElseGet(Supplier) 值为空时执行 Supplier 并返回其结果
orElseThrow(Supplier<Throwable>) 值为空时抛出自定义异常
map(Function) 值存在时对其做转换并返回新的 Optional
flatMap(Function) map 类似,但转换函数已返回 Optional,避免嵌套
filter(Predicate) 值存在且满足断言时返回自身,否则返回空 Optional

实践示例

安全地获取嵌套属性

传统写法容易嵌套多重 null 检查:

1
2
3
4
5
6
7
String city = null;
if (user != null) {
Address addr = user.getAddress();
if (addr != null) {
city = addr.getCity();
}
}

使用 Optional 链式调用:

1
2
3
4
5
Optional<User> userOpt = Optional.ofNullable(user);
String city = userOpt
.map(User::getAddress) // Optional<Address>
.map(Address::getCity) // Optional<String>
.orElse("未知城市");

条件过滤

1
2
3
// 如果age小于18,视为非法用户,返回空
Optional<User> validUser = Optional.ofNullable(user)
.filter(u -> u.getAge() >= 18);

组合默认值与异常

1
2
3
4
5
6
7
// 使用 orElse 提供默认值
String name = Optional.ofNullable(user.getName())
.orElse("匿名用户");

// 使用 orElseThrow 抛出异常
User nonNullUser = Optional.ofNullable(user)
.orElseThrow(() -> new IllegalArgumentException("User 不能为空"));

高级用法

  • 与流 (Stream) 结合:可将 Optional<T> 转成 Stream<T> 以参与流式处理:

    1
    2
    3
    4
    Stream<String> cityStream = Optional.ofNullable(user)
    .map(User::getAddress)
    .map(Address::getCity)
    .stream();
  • 缓存惰性加载:当默认值计算开销较大时,可使用 orElseGet

    1
    2
    String config = Optional.ofNullable(dbConfig.get("key"))
    .orElseGet(() -> loadFromFile("config.properties"));
  • 避免 get() 滥用:尽量通过其他 API 获取值,避免直接调用 get() 而导致潜在的 NoSuchElementException

最佳实践

  • 仅用于方法返回,不要在字段或参数上使用:Optional 并非序列化友好,也不适合作为类的成员变量或方法参数。
  • 避免嵌套 Optional:当方法可能返回 Optional 时,应使用 flatMap 而非 map 返回 Optional<Optional<T>>
  • 警惕 orElse 的副作用orElse 参数会在方法调用前就被计算,若有昂贵操作,建议使用 orElseGet
  • 合理使用 orElseThrow:对于业务逻辑必须存在的值,使用 orElseThrow 能帮助早期失败并定位问题。

总结

Optional 是 Java 8 提供的一种“类型级别的空检查”机制,通过链式 API,使得对可能缺失的值进行处理更加安全和可读。合理地使用 Optional,可以有效减少 NullPointerException 的发生,提高代码健壮性和可维护性。本文结合理论与代码示例,展示了 Optional 的基本用法、常用方法、进阶技巧及最佳实践,期望帮助读者在日常开发中更好地驾驭 Optional。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!