【Java踩坑笔记】48_用Optional消灭NPE,但别把它当万能药

📅 2026/7/17 0:05:10 👁️ 阅读次数 📝 编程学习
【Java踩坑笔记】48_用Optional消灭NPE,但别把它当万能药

48 | 用 Optional 消灭 NPE,但别把它当万能药

摘要Optional的设计目的是让返回值"可能为空"这件事显式化,而不是消灭所有null。滥用Optional反而让代码更难读。


一、问题现象

// ❌ 滥用:把 Optional 当字段类型classUser{privateOptional<String>email;// 不推荐}// ❌ 滥用:把 Optional 当方法参数publicvoidprocess(Optional<Order>order){...}// 不推荐

正确用法Optional只适合作为返回类型,告诉调用方"可能没有值"。


二、踩坑现场

场景 1:用Optional.get()不判断

// ❌ 错误:和直接 null 判断没区别Optional<User>user=userService.findById(1L);Useru=user.get();// ❌ 如果是 empty,抛 NoSuchElementException

场景 2:为了用 Optional 而用

// ❌ 不推荐:多此一举Optional<String>name=Optional.ofNullable(user.getName());if(name.isPresent()){Stringn=name.get();}// ✅ 直接判断反而更清晰if(user.getName()!=null){Stringn=user.getName();}

三、正确写法

3.1 作为返回类型(推荐)

// ✅ 正确:返回值用 Optional,显式告诉调用方"可能为空"publicOptional<User>findById(Longid){Useruser=userMapper.selectById(id);returnOptional.ofNullable(user);// 可能为空}// 调用方:Optional<User>userOpt=userService.findById(1L);userOpt.ifPresent(u->process(u));// 有值才处理

3.2 提供默认值

// ✅ orElse() — 无论是否有值,都执行括号里的代码Stringname=Optional.ofNullable(user.getName()).orElse("未知");// ✅ orElseGet() — 只有没值时才执行(推荐)Stringname=Optional.ofNullable(user.getName()).orElseGet(()->getDefaultName());// 区别:orElseGet 是懒加载,有值时不会调 getDefaultName()

3.3 链式调用

// ✅ 推荐:链式调用,代码清晰Stringcity=Optional.ofNullable(user).map(User::getAddress).map(Address::getCity).orElse("未知");// 中间任何一步为 null,都返回 "未知"(不会 NPE)

四、最佳实践

✅ Optional 使用的 4 条铁律

  1. 只作为返回类型,不要作为字段类型或方法参数
  2. 不要用get()而不判断,用isPresent()ifPresent()
  3. 链式调用(map/flatMap/filter)是 Optional 最优雅的用法
  4. orElseGet()orElse()(懒加载,不浪费计算)

🔍 不要用 Optional 的场景

场景推荐写法
字段类型直接用原类型,JPA/Hibernate 不支持 Optional 字段
方法参数直接用原类型,多个参数用 Builder
集合类型用空集合(Collections.emptyList()),不用Optional<List>

五、小结

  • Optional只适合做返回类型,显式表达"可能为空"
  • 不要当字段类型或方法参数用
  • 链式调用是 Optional 最优雅的用法(map/flatMap/filter
  • orElseGet()orElse()好(懒加载)
  • 调用get()前必须用isPresent()判断

下一篇预告:序列化版本号 serialVersionUID 不写会怎样?—— 不写 JVM 会自动生成,但不同 JVM 实现可能不一样,反序列化会失败。