Optional类

Optional类是一个容器,可以保存任何对象,并且针对NullPointerException空指针异常做了优化,保证Optional类保存的值不会是null。

因此Optional类是针对“对象可能是null也可能不是null”的场景为开发者提供了优质的解决方案,减少了繁琐的异常处理。

通过类型系统,明确地告诉 API 的使用者,一个方法的返回值可能是缺失的,并强制调用者以一种安全的方式处理这种可能性。

1.empty()

返回一个表示空值的Optional实例,即Optional实例的成员变量value=null。

2.filter()

如果value有值并且与给定条件匹配,则返回一个包含该值的Optional实例。否则返回一个表示空值的Optional实例。

所以,filter是为了获取符合条件的对象。

Optional.ofNullable(user).filter(t -> t.getUserName().equalsIgnoreCase("t"))
.ifPresent(t -> log.info("testFilter: {}", t.getUserName()));


// 比较以下的写法
if (user2 != null && user2.getUserName().equalsIgnoreCase("t")) {
user3 = user2;
} else {
user3 = new User();
}


3.get()

如果Optional实例的value是有值的,则返回value值,否则抛出异常NoSuchElementException。

4.of(T value)

要求传入的value不能为空,否则抛出NullPointerException异常。

5.ofNullable(T value)

允许传入的value为空,返回一个Optional.empty()。 

6.orElse(T other)

如果Optional实例的value是有值的,则返回value值,否则返回参数值。

7.isPresent

如果value存在(不为空)为true,否则为false。

8.ifPresent(Consumer)

如果Optional实例有值则为其调用consumer,否则不做处理。没有返回值。

所以如果需要返回value值,可以用orElse。

如果需要进一步的逻辑处理,可以用ifPresent。

相对于以下写法,使用IfPresent更简洁,而且可以和filter,map等装配。


if (user != null) {
log.info("testIfPresent: {}", user.getUserName());

}


9.map(Function mapper)

如果Optional实例的value是有值的,执行mapper函数得到返回值。

如果返回值不为空,则对返回值进行封装成Optional实例并返回。

否则,返回Optional.empty()。

String userName = Optional.ofNullable(user).map(User::getUserName).orElse("");

注意:上面语句中判断了user对象是否为空,如果user对象不为空,还进一步判断了getUserName()是否为空。

如果user对象为空或者getUserName()为空,都会通过orElse()判断。

if (ObjectUtils.isEmpty(user2)) {
userName= "";
} else {
userName = user2.getUserName();
}

10.Flatmap(Function mapper)

和map()区别在于:Flatmap要求参数的类型为Optional,或者说要求mapper函数返回值的类型为Optional,并直接返回,不再封装。

Optional<StringoptionalS Optional.ofNullable(user).flatMap(t -> Optional.ofNullable(t.getUserName()));

另外,在stream中,Flatmap用于处理集合的扁平化操作。


11. IfPresentOrElse(Consumer, Runnable)

如果Optional实例有值则为其调用consumer,否则调用Runnable。没有返回值。

Optional.ofNullable(getUsers()).ifPresentOrElse(t -> log.info("users is not null " + t), () -> {
log.info("users is null " + new ArrayList<>(Arrays.asList("A1", "A2")));
});

相对于以下写法,使用IfPresentOrElse更简洁,而且可以和filter,map等装配。


List<String> users = getUsers();
if (users != null && users.isEmpty()) {
log.info("users is not null " + users);
} else {
log.info("users is null " + new ArrayList<>(Arrays.asList("A1", "A2")));
}


使用Optional类的意义

嵌套对象String isocode = user.getAddress().getCountry().getIsocode().toUpperCase();

每一步都可能触发NullPointerException异常。

解决:

String isocode = Optional.ofNullable(user)

.flatmap(user -> user::getAddress)

.flatmap(address -> address::getCountry)

.flatmap(country -> country::getIsocode)

.orElse(“default”).toUpperCase();

使用场合

不应该用在类的字段,或者是方法的参数。这样让代码复杂而且没有必要。

可以作为方法的返回值。

错误的姿势:常见的 Optional 反模式

1: 使用 isPresent() + get()

// 典型反模式
Optional<User> optUser = findUserById(1L);
if (optUser.isPresent()) {
    User user = optUser.get();
    System.out.println("User name: " + user.getName());
} else {
    System.out.println("User not found.");
}


2: 在任何地方都滥用 get()

直接调用 get() 而不进行任何检查,是 Optional 用法中最危险的行为。如果 Optional 为空,调用 get() 会抛出 NoSuchElementException,这无异于将 NullPointerException 换成了另一种运行时异常。

// 危险!
Optional<User> optUser = findUserById(2L); // 假设没找到,返回 Optional.empty()
String name = optUser.get().getName(); // 抛出 NoSuchElementException


3: 在字段和方法参数中使用 Optional

  • 不应作为类字段: Optional 没有实现 Serializable 接口,在需要序列化的场景(如 JPA/Hibernate 实体、DTO)下会引发问题。此外,它为字段增加了一层不必要的包装,增加了复杂性。一个类要么有这个属性,要么没有,用 null 表示字段的缺失是可接受的。
  • 不应作为方法参数: 这会强迫方法的调用者去创建一个 Optional 对象来包装参数,非常不便。如果一个方法需要处理可选参数,更好的方式是方法重载
// 不推荐
public void process(Optional<String> config) { ... }

// 推荐
public void process() { ... } // 无配置
public void process(String config) { ... } // 有配置


正确的姿势:拥抱函数式 API

A. 安全地获取值或提供替代方案

orElse(T other): 如果 Optional 不为空,返回值;否则,返回 other 这个默认值。


// 如果找不到用户,返回一个 "Guest" 用户
User user = findUserById(3L).orElse(new User("Guest"));


orElseGet(Supplier<? extends T> supplier): 与 orElse 类似,但它接受一个 Supplier 函数。只有当 Optional 为空时,这个函数才会被执行。如果创建默认值的成本很高,这是最佳选择(懒加载)。


// 只有在找不到用户时,才会执行昂贵的 new DefaultUser() 操作
User user = findUserById(4L).orElseGet(() -> createDefaultUser());


orElseThrow(Supplier<? extends X> exceptionSupplier): 如果 Optional 为空,则抛出由 supplier 创建的异常。这是处理“值必须存在,否则就是错误”场景的标准方式。


// 如果找不到用户,就抛出自定义异常
User user = findUserById(5L).orElseThrow(() -> new UserNotFoundException("User not found"));


B. 当值存在时执行操作

替代 if (opt.isPresent()) { ... }。

ifPresent(Consumer<? super T> consumer): 如果值存在,就对其执行 consumer 操作。

findUserById(6L).ifPresent(user -> System.out.println("Found user: " + user.getName()));


ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) (Java 9+): 如果值存在,执行 action;否则,执行 emptyAction

findUserById(7L).ifPresentOrElse(
    user -> System.out.println("Logged in as: " + user.getName()),
    () -> System.out.println("Please log in.")
);


C. 转换值 (核心函数式特性)

mapflatMapfilter 是 Optional 的精髓,它们允许你在不破坏 Optional 封装的情况下,对内部的值进行链式操作。

map(Function<? super T, ? extends U> mapper): 如果值存在,将其通过 mapper 函数转换为另一个值,并用一个新的 Optional 包装起来。如果 Optional 为空,则返回一个空的 Optional

// 获取用户名的 Optional
Optional<String> userNameOpt = findUserById(8L).map(User::getName);


flatMap(Function<? super T, Optional<U>> mapper): 与 map 类似,但要求 mapper 函数的返回值本身就是一个 Optional

这在处理返回 Optional 的多层嵌套调用时非常有用,可以避免出现 Optional<Optional<T>> 这样的窘境。

// 假设 User 有一个返回 Optional<Profile> 的方法
// flatMap 可以将 Optional<User> -> Optional<Profile> -> Optional<String> 的链条压平
Optional<String> profileImageUrl = findUserById(9L)
    .flatMap(User::getProfile)
    .map(Profile::getImageUrl);


filter(Predicate<? super T> predicate): 如果值存在且满足 predicate 条件,则返回包含该值的 Optional;否则返回空的 Optional。

// 只处理激活状态的用户
findUserById(10L)
    .filter(User::isActive)
    .ifPresent(activeUser -> System.out.println(activeUser.getName() + " is active."));


D. 链式调用:将它们组合起来

将以上方法组合起来,可以写出极其简洁、流畅且安全的代码。


// 示例:查找ID为11的用户,如果他是管理员,则获取其大写的邮箱地址,否则返回默认的管理员邮箱
String adminEmail = findUserById(11L)
    .filter(User::isAdmin)                 // 筛选出管理员
    .map(User::getEmail)                   // 获取他的邮箱
    .map(String::toUpperCase)              // 转换为大写
    .orElse("default.admin@example.com");  // 如果以上任何一步为空,则返回默认值


这段代码一气呵成,没有任何 if-null 判断,却完美地处理了所有可能为空的情况。

总结与最佳实践清单

  • • ✅ 应当 将 Optional 作为方法的返回值,明确告知调用者该值可能缺失。
  • • ✅ 应当 使用 orElseorElseGetorElseThrow 来处理值缺失的情况。
  • • ✅ 应当 使用 mapflatMapfilter 来进行安全的链式操作。
  • • ✅ 应当 使用 ifPresent 或 ifPresentOrElse 来替代 if (isPresent())
  • • ❌ 不应 将 Optional 用作类的字段(属性)
  • • ❌ 不应 将 Optional 用作方法的参数
  • • ❌ 不应 为了调用 get() 而使用 isPresent() 进行判断。
  • • ❌ 不应 在没有检查的情况下直接调用 get()
  • • ❌ 不应 用 Optional 包装集合类型(如 Optional<List<User>>),应直接返回空集合