Optional 是 Java 8 引入的一个容器类,用于表示一个值可能存在或不存在。它是一种更优雅的方式来处理可能为 null 的值。Optional 本质上是一个包装器,用于放置可能为空的值,它可以合理而优雅地处理 null。

历史背景:null 在编程历史上极具话题性,号称是"计算机历史上最严重的错误"。Optional 的出现使 Java 对 null 的表达能力更近了一步。

在这里插入图片描述

为什么需要 Optional

传统方式的问题

// 传统方式 - 容易出现 NullPointerException,嵌套判断导致代码臃肿
public String getUserEmail(Long userId) {
    User user = userRepository.findById(userId);
    if (user != null) {
        Address address = user.getAddress();
        if (address != null) {
            String email = address.getEmail();
            if (email != null) {
                return email;
            }
        }
    }
    return "未知";
}

Optional 方式 - 函数式链式调用

// Optional 方式 - 更清晰、更安全、更简洁
public String getUserEmail(Long userId) {
    return userRepository.findById(userId)
        .map(User::getAddress)
        .map(Address::getEmail)
        .orElse("未知");
}

在这里插入图片描述

Optional 的结构

在这里插入图片描述

创建 Optional 对象

三种创建方式对比

在这里插入图片描述

代码示例与注意事项

// 1. Optional.of() - 值不能为 null,否则抛出 NullPointerException
Optional<String> opt1 = Optional.of("Hello");
// Optional<String> opt1 = Optional.of(null); // ❌ 会抛出 NPE!

# 2. Optional.ofNullable() - 值可以为 null(推荐)
Optional<String> opt2 = Optional.ofNullable(null);  // ✅ 返回 empty Optional
Optional<String> opt3 = Optional.ofNullable("Hello"); // ✅ 返回包含值的 Optional

// 3. Optional.empty() - 创建空的 Optional(业务返回值推荐)
public Optional<User> findUser(String name) {
    if (StringUtils.isEmpty(name)) {
        return Optional.empty(); // ✅ 明确返回空,而不是 null
    }
    return Optional.ofNullable(userRepository.findByName(name));
}

常用方法详解

API 推荐度速查表

方法 推荐度 说明
empty() ⭐⭐⭐⭐ 替代返回 null
of(T) ⭐⭐ 需要确保值非空
ofNullable(T) ⭐⭐⭐⭐⭐ 最常用的创建方式
get() 尽量避免直接使用
orElse(T) ⭐⭐ 适用于静态默认值
orElseGet(Supplier) ⭐⭐⭐⭐⭐ 推荐用于获取值
orElseThrow(Supplier) ⭐⭐⭐⭐ 阻塞性场景推荐
isPresent() ⭐⭐⭐ 避免用于 if 判断
ifPresent(Consumer) ⭐⭐⭐⭐ 替代 if-get 模式
map(Function) ⭐⭐⭐⭐⭐ 链式转换核心方法
flatMap(Function) ⭐⭐⭐⭐⭐ 避免嵌套 Optional
filter(Predicate) ⭐⭐⭐⭐ 条件过滤

1. 检查值是否存在

Optional<String> opt = Optional.of("Hello");

// Java 8+ - 避免在 if 判断中使用
if (opt.isPresent()) {
    System.out.println(opt.get()); // ❌ 反模式
}

// Java 11+ - isEmpty()
if (opt.isEmpty()) {
    System.out.println("值不存在");
}

// ✅ 推荐方式:使用 ifPresent
opt.ifPresent(System.out::println);

2. 获取值:orElse vs orElseGet

在这里插入图片描述

// 性能对比示例
public String getName() {
    System.out.println("方法被调用了"); // 用于观察执行情况
    return "默认名称";
}

Optional<String> opt = Optional.of("存在的值");

// ❌ orElse - 无论如何都会执行 getName()
String name1 = opt.orElse(getName()); 
// 输出:方法被调用了
// 结果:name1 = "存在的值"(但 getName() 白白执行了)

// ✅ orElseGet - 只在值不存在时才执行
String name2 = opt.orElseGet(() -> getName()); 
// 无输出(因为 opt 有值,Supplier 不会执行)
// 结果:name2 = "存在的值"

// 使用场景区分
public static final String DEFAULT_STATUS = "UNKNOWN"; // 静态常量

// ✅ 返回静态资源时使用 orElse
String status1 = opt.orElse(DEFAULT_STATUS); // 推荐

// ❌ 不要每次都创建新对象
String status2 = opt.orElse("UNKNOWN"); // 每次都会创建新 String

// ✅ 涉及方法调用、远程调用、IO 操作时使用 orElseGet
User user = userOpt.orElseGet(() -> remoteService.getDefaultUser());
String config = configOpt.orElseGet(() -> loadFromFile());

3. 条件操作

Optional<String> opt = Optional.of("Hello");

// ifPresent - 如果值存在则执行操作(替代 if-get 模式)
opt.ifPresent(value -> System.out.println(value));

// Java 9+ ifPresentOrElse - 同时处理存在和不存在的情况
opt.ifPresentOrElse(
    value -> System.out.println("存在: " + value),
    () -> System.out.println("不存在")
);

// ❌ 反模式:不要这样写
if (opt.isPresent()) {
    System.out.println(opt.get());
}

// ✅ 正确做法
opt.ifPresent(System.out::println);

4. 转换操作:map vs flatMap

在这里插入图片描述

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

// map - 转换值(函数返回普通类型)
Optional<String> upperName = name.map(String::toUpperCase);  // JOHN
Optional<Integer> length = name.map(String::length);         // 4

// flatMap - 避免 Optional<Optional<T>>(函数返回 Optional)
// 假设 Teacher 类有 Address 字段,Address 类有 City 字段

// ❌ 使用 map 会导致嵌套
Optional<Optional<Address>> nested = 
    Optional.ofNullable(teacher).map(Teacher::getAddress);

// ✅ 使用 flatMap 扁平化
Optional<String> city = Optional.ofNullable(teacher)
    .flatMap(t -> Optional.ofNullable(t.getAddress()))
    .flatMap(a -> Optional.ofNullable(a.getCity()));

// 或者更简洁(如果 getter 返回 Optional)
Optional<String> city2 = Optional.ofNullable(teacher)
    .flatMap(Teacher::getAddress)
    .flatMap(Address::getCity);

// filter - 过滤
Optional<String> filtered = name
    .filter(s -> s.length() > 3)      // 通过
    .filter(s -> s.startsWith("j"));  // 通过

5. 抛出异常:orElseThrow

// 阻塞性业务场景:必须获取到值才能继续
public User getUserById(Long id) {
    return userRepository.findById(id)
        .orElseThrow(() -> new UserNotFoundException("用户不存在: " + id));
}

// Java 10+ 可以省略参数(抛出 NoSuchElementException)
public User getUserById(Long id) {
    return userRepository.findById(id)
        .orElseThrow(); // 抛出 NoSuchElementException
}

6. 链式调用综合示例

public Optional<String> getUserCityName(Long userId) {
    return userRepository.findById(userId)
        .filter(User::isActive)             // 过滤非活跃用户
        .map(User::getAddress)              // Optional<Address>
        .filter(Address::isValid)           // 过滤无效地址
        .map(Address::getCity)              // Optional<City>
        .map(City::getName)                 // Optional<String>
        .filter(name -> !name.isEmpty());   // 过滤空名称
}

最佳实践

基本原则

在这里插入图片描述

1. 返回值使用 Optional(✅ 推荐)

在这里插入图片描述

// ✅ 好的实践:明确表示返回值可能为空
public Optional<User> findUserById(Long id) {
    return userRepository.findById(id);
}

// 调用方可以清晰地处理空值
Optional<User> userOpt = service.findUserById(1L);
userOpt.ifPresent(user -> System.out.println(user.getName()));

// ❌ 不推荐:返回 null 给调用方带来负担
public User findUserById(Long id) {
    return userRepository.findById(id); // 可能返回 null
}

2. 不要在字段中使用 Optional(❌ 反模式)

// ❌ 不推荐:序列化问题、占用额外内存
public class User {
    private Optional<String> nickname;  // 不要这样做
    private Optional<Address> address;  // 不要这样做
}

// ✅ 推荐:字段可为 null,通过 getter 返回 Optional
public class User {
    private String nickname;  // 可以为 null
    private Address address;  // 可以为 null
    
    public Optional<String> getNickname() {
        return Optional.ofNullable(nickname);
    }
    
    public Optional<Address> getAddress() {
        return Optional.ofNullable(address);
    }
}

3. 不要用于方法参数(❌ 反模式)

// ❌ 不推荐:增加调用复杂度
public void setUserType(Optional<UserType> userType) {
    this.userType = userType.orElse(UserType.NORMAL);
}

// ✅ 推荐:使用方法重载
public void setUserType(UserType userType) {
    this.userType = userType != null ? userType : UserType.NORMAL;
}

// 或者提供多个重载方法
public void createUser(String name) {
    createUser(name, null);
}

public void createUser(String name, UserType userType) {
    // 实现逻辑
}

4. 不要在集合中使用 Optional(❌ 反模式)

// ❌ 不推荐:集合本身就能表达空
List<Optional<String>> list = new ArrayList<>();
Map<String, Optional<User>> map = new HashMap<>();

// ✅ 推荐:直接使用集合的空表达
List<String> list = new ArrayList<>(); // 空列表本身就是空的表达
Map<String, User> map = new HashMap<>(); // 使用 getOrDefault()
User user = map.getOrDefault("key", defaultUser);

5. 合理使用 orElse 和 orElseGet

// 静态常量
public static final String DEFAULT_STATUS = "UNKNOWN";
public static final User GUEST_USER = new User("Guest");

// ✅ 静态资源用 orElse
String status = Optional.ofNullable(getStatus()).orElse(DEFAULT_STATUS);
User user = Optional.ofNullable(getUser()).orElse(GUEST_USER);

// ✅ 方法调用、计算、IO 操作用 orElseGet
String config = configOpt.orElseGet(() -> loadConfigFromFile());
User admin = userOpt.orElseGet(() -> createDefaultAdmin());
String data = dataOpt.orElseGet(() -> fetchFromRemote());

常见反模式

反模式速查表

反模式 问题 正确做法
if (opt.isPresent()) opt.get() 等同于判空,失去 Optional 意义 使用 ifPresent()orElse()
返回 null 的 Optional 违背 Optional 设计初衷 返回 Optional.empty()
字段使用 Optional 序列化问题、内存浪费 字段为普通类型,getter 返回 Optional
参数使用 Optional 增加调用复杂度 使用方法重载或直接传 null
过度包装简单判空 增加代码复杂度 简单场景直接判空
直接使用 get() 可能抛出 NoSuchElementException 使用 orElse 系列方法

1. 不要使用 isPresent() + get()

// ❌ 反模式:本质上还是 if-null 判断
if (opt.isPresent()) {
    String value = opt.get();
    System.out.println(value);
} else {
    System.out.println("默认值");
}

// ✅ 正确做法 1:使用 ifPresent
opt.ifPresent(System.out::println);

// ✅ 正确做法 2:使用 ifPresentOrElse (Java 9+)
opt.ifPresentOrElse(
    System.out::println,
    () -> System.out.println("默认值")
);

// ✅ 正确做法 3:使用 orElse
String value = opt.orElse("默认值");
System.out.println(value);

2. 不要返回 null 的 Optional

// ❌ 反模式:永远不要返回 null
public Optional<User> findUser(Long id) {
    if (id == null) {
        return null;  // ❌ 这违背了 Optional 的设计初衷
    }
    return userRepository.findById(id);
}

// ✅ 正确做法:返回 empty Optional
public Optional<User> findUser(Long id) {
    if (id == null) {
        return Optional.empty();  // ✅ 明确表示没有值
    }
    return userRepository.findById(id);
}

3. 避免过度使用

// ❌ 过度使用:简单场景反而更复杂
String name = Optional.ofNullable(user)
    .map(User::getName)
    .orElse("未知");

// ✅ 简单情况直接判空更清晰
String name = user != null ? user.getName() : "未知";

// ✅ Optional 适合复杂的链式调用
String cityName = Optional.ofNullable(user)
    .map(User::getAddress)
    .filter(Address::isValid)
    .map(Address::getCity)
    .map(City::getName)
    .orElse("未知");

性能考量

orElse vs orElseGet 性能对比

@Test
public void performanceTest() {
    Optional<String> opt = Optional.of("value");
    
    // orElse - 总是执行,即使有值
    long start1 = System.nanoTime();
    for (int i = 0; i < 1000000; i++) {
        opt.orElse(expensiveOperation()); // expensiveOperation 总是执行
    }
    long time1 = System.nanoTime() - start1;
    
    // orElseGet - 只在没有值时执行
    long start2 = System.nanoTime();
    for (int i = 0; i < 1000000; i++) {
        opt.orElseGet(() -> expensiveOperation()); // 不会执行
    }
    long time2 = System.nanoTime() - start2;
    
    System.out.println("orElse: " + time1 + " ns");
    System.out.println("orElseGet: " + time2 + " ns");
    // orElseGet 性能明显优于 orElse
}

Optional 的开销

// Optional 会创建额外对象,在性能敏感场景需要考虑
// ❌ 不推荐:在循环中大量创建 Optional
for (int i = 0; i < 1000000; i++) {
    Optional<String> opt = Optional.ofNullable(getValue(i));
    opt.ifPresent(this::process);
}

// ✅ 推荐:直接判空
for (int i = 0; i < 1000000; i++) {
    String value = getValue(i);
    if (value != null) {
        process(value);
    }
}

Optional 方法流程图

在这里插入图片描述

实际应用场景

场景 1: 数据库查询

// Repository 层返回 Optional
public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findById(Long id);
    Optional<User> findByEmail(String email);
}

// Service 层处理 Optional
public String getUserEmailOrDefault(Long userId) {
    return userRepository.findById(userId)
        .map(User::getEmail)
        .filter(email -> email.contains("@"))
        .orElse("no-email@example.com");
}

// Controller 层
public UserDTO getUserInfo(Long userId) {
    return userService.findById(userId)
        .map(this::convertToDTO)
        .orElseThrow(() -> new ResourceNotFoundException("用户不存在"));
}

场景 2: 配置读取

public class ConfigService {
    private Map<String, String> configMap = new HashMap<>();
    
    // 返回 Optional 明确表示配置可能不存在
    public Optional<String> getConfig(String key) {
        return Optional.ofNullable(configMap.get(key));
    }
    
    // 使用方可以灵活处理
    public int getTimeout() {
        return getConfig("timeout")
            .map(Integer::parseInt)
            .filter(v -> v > 0)
            .orElseGet(() -> getDefaultTimeout());
    }
    
    public String getDatabaseUrl() {
        return getConfig("db.url")
            .orElseThrow(() -> new ConfigurationException("缺少数据库配置"));
    }
}

场景 3: Stream 操作结合

public Optional<User> findFirstAdminUser() {
    return userList.stream()
        .filter(User::isAdmin)
        .filter(User::isActive)
        .findFirst();  // 返回 Optional<User>
}

// 链式处理
public String getFirstAdminEmail() {
    return userList.stream()
        .filter(User::isAdmin)
        .findFirst()
        .map(User::getEmail)
        .orElse("admin@example.com");
}

场景 4: 复杂业务逻辑

public class OrderService {
    // 获取订单的收货地址城市名称
    public Optional<String> getOrderShippingCity(Long orderId) {
        return orderRepository.findById(orderId)
            .filter(Order::isPaid)                    // 只处理已支付订单
            .map(Order::getShippingAddress)          // 获取收货地址
            .filter(Address::isComplete)             // 地址信息完整
            .map(Address::getCity)                   // 获取城市
            .filter(city -> !city.trim().isEmpty()); // 城市名称非空
    }
    
    // 使用示例
    public void processOrder(Long orderId) {
        getOrderShippingCity(orderId)
            .ifPresentOrElse(
                city -> shippingService.sendToCity(city),
                () -> notificationService.notifyIncompleteAddress(orderId)
            );
    }
}

总结

Optional 的核心优势

在这里插入图片描述

使用指南总结

何时使用 Optional:

  • ✅ 作为方法的返回值类型
  • ✅ 处理可能为空的业务逻辑
  • ✅ 需要链式调用和函数式编程风格
  • ✅ 明确表达"值可能不存在"的语义

何时不使用 Optional:

  • ❌ 不要用作类的成员变量
  • ❌ 不要用作方法参数
  • ❌ 不要用在集合元素中
  • ❌ 简单的判空场景(过度设计)
  • ❌ 性能敏感的高频调用

记住这些要点

  1. Optional 不是银弹:它是为了更好地表达"可能没有值"的语义,而不是替代所有的 null 检查
  2. **优先使用 **orElseGet:避免 orElse 的性能陷阱
  3. **避免 **isPresent() + get():使用 ifPresentmaporElse 等方法
  4. 永远不要返回 null:返回 Optional.empty()
  5. 合理使用:在适合的场景使用,不要过度设计

最佳实践总结:Optional 让空值处理更加优雅和安全,但需要理解其设计意图和使用场景,避免滥用。合理使用可以大大提升代码质量,减少 NPE,节省大量调试时间。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐