Java Optional

Java Optional 很好,但别把它当万能锤子

Java Optional 从入门到最佳实践

适用版本:Java 8+

重点覆盖:Optional 介绍、为什么要引入 Optional、基本使用方法、最佳实践、不合理使用场景。

一、Optional 介绍 Introduction to Optional

Optional<T> 是 Java 8 引入的一个容器类,位于 java.util 包下。它表示一个值可能存在,也可能不存在:

  • 有值:Optional.of(value)
  • 无值:Optional.empty()
  • 值可能为 nullOptional.ofNullable(value)

从语义上看,Optional<T> 不是为了“把所有 null 都包装起来”,而是为了在方法返回值中明确表达:

这个方法可能找不到结果,调用方必须认真处理这种情况。

官方 API 文档中也明确提到,Optional 主要适合用作方法返回类型,用来表达“没有结果”的情况,尤其是直接返回 null 容易导致错误时。

一个最典型的例子:

1
2
3
4
public Optional<User> findById(Long userId) {
User user = userRepository.findById(userId);
return Optional.ofNullable(user);
}

调用方看到返回值是 Optional<User>,就会意识到:

1
Optional<User> userOptional = userService.findById(1L);

这里不是“肯定有用户”,而是“可能有,也可能没有”。这就是 Optional 最大的价值:让空值语义显式化

二、为什么要引入 Optional?Why use Optional?

1. 减少隐式 null 带来的 NPE 风险

传统 Java 代码中,方法返回 null 很常见:

1
2
3
public User findById(Long userId) {
return userRepository.findById(userId);
}

调用方如果忘记判空:

1
2
User user = userService.findById(1L);
String name = user.getName();

一旦 usernull,就会抛出 NullPointerException

使用 Optional 后,返回值的类型本身就在提醒调用方:

1
2
3
public Optional<User> findById(Long userId) {
return Optional.ofNullable(userRepository.findById(userId));
}

调用方必须选择一种处理方式:

1
2
3
String name = userService.findById(1L)
.map(User::getName)
.orElse("Unknown");

这并不是说 Optional 能彻底消灭 NPE,而是说它能把“可能为空”从隐藏约定变成显式类型。

2. 提高 API 的表达力

下面两个方法签名,表达力完全不同:

1
public User getCurrentUser();
1
public Optional<User> getCurrentUser();

第一个方法看不出来是否可能返回 null。调用方只能看文档、看实现,或者踩坑。

第二个方法直接告诉你:当前用户可能不存在。

在业务代码中,Optional 很适合表达这些语义:

  • 根据 ID 查询数据,数据可能不存在。
  • 从缓存中读取值,缓存可能未命中。
  • 从请求上下文中获取用户,用户可能未登录。
  • 从配置中读取可选配置,配置可能未设置。
  • 从集合中查找第一个符合条件的元素,可能找不到。

例如:

1
2
3
4
5
public Optional<Coupon> findAvailableCoupon(Long userId) {
return couponRepository.findByUserId(userId).stream()
.filter(Coupon::isAvailable)
.findFirst();
}

这里的 Optional<Coupon> 比返回 null 更清楚,也比抛异常更自然,因为“没有可用优惠券”通常不是异常,而是一种正常业务结果。

3. 配合 Stream 和函数式写法

Java 8 同时引入了 Lambda、Stream 和 Optional。它们放在一起时,可以让一些判空、过滤、转换逻辑更紧凑:

1
2
3
4
String email = userService.findById(userId)
.filter(User::isActive)
.map(User::getEmail)
.orElse("no-email@example.com");

这段代码表达的是:

  1. 查询用户。
  2. 用户存在并且是启用状态。
  3. 获取邮箱。
  4. 如果任一步没有结果,就返回默认邮箱。

如果用传统写法,大概会变成:

1
2
3
4
5
6
User user = userService.findById(userId);
String email = "no-email@example.com";

if (user != null && user.isActive()) {
email = user.getEmail();
}

传统写法也没错,但 Optional 的链式写法更适合表达“可能中断的值转换流程”。

三、Optional 的基本使用方法 Basic usage of Optional

1. 创建 Optional

Optional.empty()

创建一个空的 Optional:

1
Optional<User> emptyUser = Optional.empty();

注意:不要用 == 判断某个 Optional 是否等于 Optional.empty()。官方文档说明,不能依赖 Optional.empty() 是单例对象,应使用 isPresent()isEmpty() 判断。

1
2
3
4
5
6
7
8
9
// 不推荐
if (optional == Optional.empty()) {
// ...
}

// 推荐
if (optional.isEmpty()) {
// ...
}

isEmpty() 是 Java 11 引入的。如果你还在 Java 8,可以使用:

1
2
3
if (!optional.isPresent()) {
// ...
}

Optional.of(value)

创建一个一定有值的 Optional:

1
Optional<String> name = Optional.of("Mario");

如果传入 null,会直接抛出 NullPointerException

1
Optional<String> name = Optional.of(null); // NPE

所以 Optional.of() 适合用于你非常确定值不为 null 的场景。

Optional.ofNullable(value)

当值可能为 null 时,使用 ofNullable()

1
2
String name = getNameFromDatabase();
Optional<String> optionalName = Optional.ofNullable(name);

如果 name 不为 null,得到一个有值的 Optional;如果为 null,得到 Optional.empty()

这也是把旧代码中的可空返回值转换为 Optional 的常用方式。

2. 判断是否有值

1
2
3
4
5
6
Optional<User> optionalUser = userService.findById(userId);

if (optionalUser.isPresent()) {
User user = optionalUser.get();
System.out.println(user.getName());
}

这段代码可以运行,但它不是最推荐的 Optional 写法。因为 isPresent() + get() 本质上只是把 user != null 换了一身新衣服,业务逻辑复杂后还是容易变得啰嗦。

更推荐使用 ifPresent()map()orElse()orElseThrow() 等方法。

3. ifPresent()

有值时执行逻辑,无值时什么都不做:

1
2
userService.findById(userId)
.ifPresent(user -> log.info("found user: {}", user.getName()));

适合处理“有就做,没有就算了”的场景。

4. ifPresentOrElse()

Java 9 引入 ifPresentOrElse(),可以同时处理有值和无值:

1
2
3
4
5
userService.findById(userId)
.ifPresentOrElse(
user -> log.info("found user: {}", user.getName()),
() -> log.warn("user not found, userId={}", userId)
);

如果项目还在 Java 8,可以继续用普通 if,不用为了链式写法强行绕。

5. orElse()

无值时返回默认值:

1
2
3
String username = userService.findById(userId)
.map(User::getName)
.orElse("anonymous");

orElse() 的参数会先被计算出来,再传给方法。所以如果默认值创建成本较高,或者带有副作用,就要小心。

1
String username = optionalName.orElse(createDefaultName());

即使 optionalName 有值,createDefaultName() 也会先执行。

6. orElseGet()

无值时再通过 Supplier 延迟获取默认值:

1
String username = optionalName.orElseGet(() -> createDefaultName());

如果 optionalName 有值,createDefaultName() 不会执行。

所以经验上可以这么选:

场景 推荐写法
默认值是简单常量 orElse("anonymous")
默认值需要查询、计算、创建对象 orElseGet(() -> loadDefault())
默认值方法有日志、数据库调用、远程调用等副作用 orElseGet(...)

7. orElseThrow()

无值时抛出异常:

1
2
User user = userService.findById(userId)
.orElseThrow(() -> new BusinessException("用户不存在"));

这很适合“查不到就是错误”的场景,例如更新用户资料前必须先找到用户:

1
2
3
4
5
6
7
public void updateUsername(Long userId, String username) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new BusinessException("用户不存在"));

user.setUsername(username);
userRepository.save(user);
}

Java 10 开始,orElseThrow() 可以不传异常 Supplier,默认抛 NoSuchElementException

1
User user = optionalUser.orElseThrow();

不过在业务系统中,一般更建议抛出带明确业务信息的异常。

8. map()

map() 用来把 Optional 中的值转换成另一个值:

1
2
Optional<String> username = userService.findById(userId)
.map(User::getName);

如果用户存在,则返回 Optional<String>;如果用户不存在,则返回 Optional.empty()

也可以继续接默认值:

1
2
3
String username = userService.findById(userId)
.map(User::getName)
.orElse("anonymous");

map() 的一个重要特点是:如果映射函数返回 null,结果会变成 Optional.empty()

1
2
Optional<String> email = userService.findById(userId)
.map(User::getEmail);

如果 getEmail() 返回 null,最终就是空 Optional。

9. flatMap()

如果转换函数本身已经返回 Optional,就使用 flatMap(),避免出现 Optional<Optional<T>>

错误示例:

1
2
Optional<Optional<Address>> address = userService.findById(userId)
.map(user -> addressService.findDefaultAddress(user.getId()));

推荐写法:

1
2
Optional<Address> address = userService.findById(userId)
.flatMap(user -> addressService.findDefaultAddress(user.getId()));

可以这样理解:

  • map():普通值转换。
  • flatMap():转换函数本身返回 Optional 时使用。

10. filter()

filter() 用来在 Optional 有值时继续判断条件,不满足就变成空:

1
2
Optional<User> activeUser = userService.findById(userId)
.filter(User::isActive);

实际业务例子:

1
2
3
4
5
6
public Optional<Coupon> findUsableCoupon(Long couponId) {
return couponRepository.findById(couponId)
.filter(Coupon::isEnabled)
.filter(coupon -> !coupon.isExpired())
.filter(coupon -> coupon.getRemainCount() > 0);
}

这段代码表达很清楚:

  1. 优惠券存在。
  2. 优惠券启用。
  3. 优惠券未过期。
  4. 优惠券还有库存。

任何条件不满足,都返回空 Optional。

11. or()

Java 9 引入 or(),用于在当前 Optional 为空时,返回另一个 Optional:

1
2
Optional<User> user = findFromCache(userId)
.or(() -> findFromDatabase(userId));

它适合表达多级查找:

1
2
3
Optional<Config> config = findFromUserConfig(key)
.or(() -> findFromTenantConfig(key))
.or(() -> findFromGlobalConfig(key));

12. stream()

Java 9 引入 Optional.stream(),可以把 Optional 转成 0 个或 1 个元素的 Stream。

例如,现在有一个 List<Optional<User>>

1
2
3
4
5
List<Optional<User>> optionalUsers = List.of(
Optional.of(new User("Mario")),
Optional.empty(),
Optional.of(new User("Luigi"))
);

可以这样提取出真正存在的用户:

1
2
3
List<User> users = optionalUsers.stream()
.flatMap(Optional::stream)
.toList();

在 Java 8 中,可以用这种写法:

1
2
3
4
List<User> users = optionalUsers.stream()
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());

四、Optional 的最佳实践 Best Practices for Optional

1. 优先用于方法返回值

Optional 最适合的位置是方法返回值,尤其是查询、查找、匹配、解析这类“可能没有结果”的方法。

推荐:

1
2
3
public Optional<User> findById(Long userId) {
return Optional.ofNullable(userMapper.selectById(userId));
}

不推荐:

1
2
3
public User findById(Long userId) {
return userMapper.selectById(userId); // 可能返回 null,但方法签名看不出来
}

命名上也建议区分语义:

方法名 建议返回值 语义
findById Optional<User> 找不到是正常情况
getById User 语义上更像必须拿到,拿不到可抛异常
existsById boolean 只关心是否存在
listByUserId List<Order> 集合为空即可,不需要 Optional 包集合

2. Optional 变量本身不要为 null

官方文档明确说明:类型为 Optional 的变量不应该为 null,它应该始终指向一个 Optional 实例。

不推荐:

1
Optional<User> optionalUser = null;

推荐:

1
Optional<User> optionalUser = Optional.empty();

如果 Optional 本身还能为 null,那就会出现一种非常尴尬的情况:

1
2
3
if (optionalUser != null && optionalUser.isPresent()) {
// ...
}

这就等于把 Optional 的价值吃回去了,属于“买了安全带但放后备箱里”的写法。

3. 不要无脑使用 get()

get() 在 Optional 为空时会抛出 NoSuchElementException。如果你写的是:

1
User user = optionalUser.get();

那你必须非常确定它一定有值。否则这和直接空指针没本质区别,只是换了一个异常。

不推荐:

1
User user = userService.findById(userId).get();

推荐:

1
2
User user = userService.findById(userId)
.orElseThrow(() -> new BusinessException("用户不存在"));

或者:

1
2
3
String username = userService.findById(userId)
.map(User::getName)
.orElse("anonymous");

4. 不要把 Optional 当成集合

Optional 最多只有一个值,它不是集合,也不是 List 的替代品。

如果结果可能有多个,用集合:

1
2
3
public List<Order> listOrders(Long userId) {
return orderRepository.listByUserId(userId);
}

如果没有订单,返回空集合即可:

1
return Collections.emptyList();

不推荐:

1
2
3
public Optional<List<Order>> listOrders(Long userId) {
return Optional.ofNullable(orderRepository.listByUserId(userId));
}

Optional<List<T>> 会让调用方同时处理两层含义:

  1. Optional 是否为空。
  2. List 是否为空。

这通常是多余复杂度。

5. 根据业务语义选择 Optional、异常或空集合

不是所有“没有值”都应该用 Optional。

场景 推荐返回
根据 ID 查询,找不到是正常情况 Optional<T>
根据 ID 获取,找不到是业务错误 抛业务异常或返回 T
查询列表,无数据 空集合
判断是否存在 boolean
计算结果可能不存在 Optional<T>
参数非法 抛异常

例子:

1
2
3
4
5
6
7
8
9
10
11
12
public Optional<User> findById(Long userId) {
return userRepository.findById(userId);
}

public User getById(Long userId) {
return userRepository.findById(userId)
.orElseThrow(() -> new BusinessException("用户不存在"));
}

public List<Order> listOrders(Long userId) {
return orderRepository.listByUserId(userId);
}

这样 API 语义更干净。

6. 默认值创建成本高时使用 orElseGet()

看这段代码:

1
User user = optionalUser.orElse(createGuestUser());

即使 optionalUser 有值,createGuestUser() 也会先执行。

如果创建默认用户很轻量,问题不大。但如果里面有数据库查询、远程调用、日志埋点、对象构建等逻辑,就会造成不必要的开销或副作用。

推荐:

1
User user = optionalUser.orElseGet(() -> createGuestUser());

或者更简洁:

1
User user = optionalUser.orElseGet(this::createGuestUser);

7. 使用 map / flatMap 处理多层取值

传统多层判空:

1
2
3
4
5
6
7
String city = null;

if (user != null
&& user.getProfile() != null
&& user.getProfile().getAddress() != null) {
city = user.getProfile().getAddress().getCity();
}

Optional 写法:

1
2
3
4
5
String city = Optional.ofNullable(user)
.map(User::getProfile)
.map(Profile::getAddress)
.map(Address::getCity)
.orElse("Unknown");

这类写法适合读取链路比较清楚的值。不过也别过度链式化,如果业务逻辑有很多分支、日志、异常、状态变更,普通 if 往往更易读。

8. 对基础类型优先考虑 OptionalInt / OptionalLong / OptionalDouble

如果要表达基础类型的可选值,可以使用:

  • OptionalInt
  • OptionalLong
  • OptionalDouble

例如:

1
2
3
4
5
public OptionalInt findMaxAge(List<User> users) {
return users.stream()
.mapToInt(User::getAge)
.max();
}

相比 Optional<Integer>OptionalInt 可以避免装箱拆箱,更贴合基础类型场景。

9. 在 Stream 中善用 Optional.stream()

Java 9+ 可以这样把多个 Optional 展平:

1
2
3
4
List<User> users = userIds.stream()
.map(userService::findById)
.flatMap(Optional::stream)
.toList();

这比下面这种写法更清爽:

1
2
3
4
5
List<User> users = userIds.stream()
.map(userService::findById)
.filter(Optional::isPresent)
.map(Optional::get)
.toList();

10. 不要为了“看起来高级”而牺牲可读性

Optional 不是比赛谁能把代码写成一行。

不推荐:

1
2
3
4
5
6
7
return Optional.ofNullable(request)
.map(Request::getUser)
.filter(User::isActive)
.map(User::getRole)
.filter(role -> role.hasPermission("ORDER_CREATE"))
.map(role -> orderService.create(request))
.orElseThrow(() -> new BusinessException("无权限或请求非法"));

这段代码的问题是:不同失败原因被混在一起了。

更清晰的写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if (request == null) {
throw new BusinessException("请求不能为空");
}

User user = request.getUser();
if (user == null || !user.isActive()) {
throw new BusinessException("用户无效");
}

Role role = user.getRole();
if (role == null || !role.hasPermission("ORDER_CREATE")) {
throw new BusinessException("无下单权限");
}

return orderService.create(request);

结论很简单:Optional 适合处理值转换,不适合隐藏复杂业务分支。

五、不合理的使用场景 Inappropriate Use Cases

1. 不建议用作方法参数

不推荐:

1
2
3
public void sendEmail(Optional<String> email) {
email.ifPresent(this::doSendEmail);
}

调用方会很别扭:

1
sendEmail(Optional.ofNullable(user.getEmail()));

更直接的方式:

1
2
3
4
5
6
public void sendEmail(String email) {
if (email == null || email.isBlank()) {
return;
}
doSendEmail(email);
}

或者把语义拆开:

1
2
3
4
5
public void sendEmail(User user) {
if (user.hasEmail()) {
doSendEmail(user.getEmail());
}
}

为什么不建议用作参数?

  • 调用方负担变重。
  • 并不能阻止传入 null,因为别人仍然可以调用 sendEmail(null)
  • 参数是否可选,通常可以通过方法重载、默认值、构建器、配置对象表达。

2. 不建议用作实体类字段

不推荐:

1
2
3
4
public class User {
private Long id;
private Optional<String> nickname;
}

更推荐:

1
2
3
4
public class User {
private Long id;
private String nickname;
}

原因:

  • Optional 官方定位主要是方法返回值,而不是字段。
  • Optional 是值基类,不应该依赖对象身份,也不适合做同步锁。
  • ORM、JSON 序列化、Bean 映射等框架处理 Optional 字段时可能需要额外适配。
  • 字段是数据模型的一部分,而 Optional 更像 API 边界上的语义提示。

如果你想表达字段可为空,数据库、校验注解、文档和类型约定更合适:

1
2
3
4
5
6
7
8
public class User {
private Long id;

/**
* 用户昵称,可为空。
*/
private String nickname;
}

或者使用 Bean Validation:

1
2
3
4
5
6
public class CreateUserRequest {
@NotBlank
private String username;

private String nickname;
}

3. 不建议用在 DTO / VO / RPC 入参出参字段中

不推荐:

1
2
3
4
public class UserDTO {
private Long id;
private Optional<String> nickname;
}

在接口对象里使用 Optional 字段,很容易引发几个问题:

  • 前端看到的 JSON 结构可能不符合预期。
  • OpenAPI / Swagger / Apifox 文档可能变得奇怪。
  • RPC、序列化框架可能不按你想的方式处理。
  • 字段是否存在、字段是否为 null、Optional 是否 empty,语义容易混在一起。

接口对象更建议保持朴素:

1
2
3
4
public class UserDTO {
private Long id;
private String nickname;
}

如果需要表达字段是否必填,使用文档、注解或协议层能力:

1
2
3
4
5
6
public class UpdateUserRequest {
@NotNull
private Long id;

private String nickname;
}

4. 不建议返回 Optional 集合

不推荐:

1
2
3
public Optional<List<Order>> findOrders(Long userId) {
return Optional.ofNullable(orderRepository.listByUserId(userId));
}

推荐:

1
2
3
4
public List<Order> listOrders(Long userId) {
List<Order> orders = orderRepository.listByUserId(userId);
return orders == null ? Collections.emptyList() : orders;
}

集合本身已经可以表达“没有数据”:

  • List 为空:查到了 0 条。
  • Map 为空:没有键值。
  • Set 为空:没有元素。

没必要再包一层 Optional。

5. 不建议滥用 isPresent() + get()

不推荐:

1
2
3
4
5
6
7
8
Optional<User> optionalUser = userService.findById(userId);

if (optionalUser.isPresent()) {
User user = optionalUser.get();
return user.getName();
}

return "anonymous";

推荐:

1
2
3
return userService.findById(userId)
.map(User::getName)
.orElse("anonymous");

当然,不是说 isPresent() 永远不能用。如果逻辑非常复杂,普通 if 可能更清楚:

1
2
3
4
5
6
7
8
9
10
Optional<User> optionalUser = userService.findById(userId);

if (optionalUser.isEmpty()) {
log.warn("user not found, userId={}", userId);
return;
}

User user = optionalUser.get();
auditLog(user);
sendNotification(user);

这里 isEmpty() 加提前返回就很直观。最佳实践不是“禁用 if”,而是别把 Optional 写回旧式判空的老路。

6. 不建议用 Optional 包装每一个可能为 null 的局部变量

不推荐:

1
2
3
Optional<String> name = Optional.ofNullable(user.getName());
Optional<String> email = Optional.ofNullable(user.getEmail());
Optional<String> phone = Optional.ofNullable(user.getPhone());

这会让代码到处都是包装和拆包,读起来反而更重。

局部变量能用普通判空讲清楚时,就用普通判空:

1
2
3
4
5
6
String email = user.getEmail();
if (email == null || email.isBlank()) {
return;
}

sendEmail(email);

Optional 更适合出现在方法边界上,而不是每一行内部实现里。

7. 不建议把 Optional 当作异常替代品

Optional 表达的是“没有结果”,不是“程序出错”。

推荐使用 Optional:

1
2
3
public Optional<User> findByEmail(String email) {
return userRepository.findByEmail(email);
}

不推荐使用 Optional:

1
2
3
4
5
6
public Optional<Order> createOrder(CreateOrderCommand command) {
if (command == null) {
return Optional.empty();
}
// ...
}

command == null 是参数非法,不是“没有订单结果”。这种情况应该抛异常:

1
2
3
4
5
6
public Order createOrder(CreateOrderCommand command) {
if (command == null) {
throw new IllegalArgumentException("command must not be null");
}
// ...
}

判断标准:

  • 没查到、没匹配到、没配置:可以考虑 Optional。
  • 参数非法、状态错误、数据不一致、调用失败:应该考虑异常。

8. 不建议在性能敏感路径中过度使用 Optional

Optional 是对象包装。大多数业务系统里,这点开销可以忽略。但在高频循环、底层工具、性能敏感代码里,过度创建 Optional 可能没有必要。

不推荐:

1
2
3
4
for (int i = 0; i < values.length; i++) {
Optional<Integer> value = Optional.of(values[i]);
sum += value.orElse(0);
}

推荐:

1
2
3
for (int value : values) {
sum += value;
}

在业务层,先考虑可读性;在底层热点代码里,再考虑对象分配和性能。

9. 不建议对 Optional 做身份相关操作

Optional 是值基类,官方文档提醒不应依赖对象身份,不要对 Optional 实例做同步。

不推荐:

1
2
3
synchronized (optionalUser) {
// ...
}

也不推荐:

1
2
3
if (optionalUser == Optional.empty()) {
// ...
}

推荐:

1
2
3
if (optionalUser.isEmpty()) {
// ...
}

或者 Java 8:

1
2
3
if (!optionalUser.isPresent()) {
// ...
}

六、常见业务写法示例

示例 1:查询用户,找不到返回默认展示名

1
2
3
4
5
6
public String getDisplayName(Long userId) {
return userRepository.findById(userId)
.map(User::getNickname)
.filter(nickname -> !nickname.isBlank())
.orElse("匿名用户");
}

示例 2:更新数据,找不到直接抛业务异常

1
2
3
4
5
6
7
public void disableUser(Long userId) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new BusinessException("用户不存在"));

user.disable();
userRepository.save(user);
}

示例 3:缓存未命中时查数据库

Java 9+:

1
2
3
4
public Optional<User> findUser(Long userId) {
return findFromCache(userId)
.or(() -> findFromDatabase(userId));
}

Java 8:

1
2
3
4
5
6
7
public Optional<User> findUser(Long userId) {
Optional<User> cachedUser = findFromCache(userId);
if (cachedUser.isPresent()) {
return cachedUser;
}
return findFromDatabase(userId);
}

示例 4:多级属性安全读取

1
2
3
4
5
6
7
public String getCity(User user) {
return Optional.ofNullable(user)
.map(User::getProfile)
.map(Profile::getAddress)
.map(Address::getCity)
.orElse("Unknown");
}

示例 5:Stream 中过滤不存在的数据

1
2
3
4
5
6
public List<User> findExistingUsers(List<Long> userIds) {
return userIds.stream()
.map(userRepository::findById)
.flatMap(Optional::stream)
.toList();
}

如果是 Java 8:

1
2
3
4
5
6
7
public List<User> findExistingUsers(List<Long> userIds) {
return userIds.stream()
.map(userRepository::findById)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
}

七、Optional 使用决策表

问题 建议
方法可能查不到单个对象? 返回 Optional<T>
方法查询列表但可能无数据? 返回空集合,不要返回 Optional<List<T>>
参数可选? 优先考虑重载、Builder、配置对象,不建议用 Optional 参数
字段可为空? 字段保持原始类型,用注解、文档、数据库约束表达
查不到是业务错误? orElseThrow() 抛业务异常
默认值创建很便宜? orElse(defaultValue)
默认值创建很贵或有副作用? orElseGet(supplier)
转换普通值? map()
转换函数返回 Optional? flatMap()
基础类型可选? OptionalIntOptionalLongOptionalDouble
Optional 自身是否可以为 null? 不可以,使用 Optional.empty()

八、一句话总结

Optional 的核心价值不是“消灭 null”,而是让可能缺失的返回值变得显式、可读、可组合

写 Optional 时记住三句话:

  1. 优先用于方法返回值。
  2. 不要让 Optional 自己变成 null。
  3. 不要为了链式而链式,可读性永远排第一。

Optional 用得好,代码会更清楚;用得过头,代码会更绕。它是一把小刀,不是瑞士军刀,更不是 Java 世界的全自动扫雷器。

参考资料


Java Optional
https://allendericdalexander.github.io/2026/06/11/java/utils/java-optional-best-practices/
作者
AtLuoFu
发布于
2026年6月11日
许可协议