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 条铁律
- 只作为返回类型,不要作为字段类型或方法参数
- 不要用
get()而不判断,用isPresent()或ifPresent() - 链式调用(
map/flatMap/filter)是 Optional 最优雅的用法 orElseGet()比orElse()好(懒加载,不浪费计算)
🔍 不要用 Optional 的场景
| 场景 | 推荐写法 |
|---|---|
| 字段类型 | 直接用原类型,JPA/Hibernate 不支持 Optional 字段 |
| 方法参数 | 直接用原类型,多个参数用 Builder |
| 集合类型 | 用空集合(Collections.emptyList()),不用Optional<List> |
五、小结
Optional只适合做返回类型,显式表达"可能为空"- 不要当字段类型或方法参数用
- 链式调用是 Optional 最优雅的用法(
map/flatMap/filter) orElseGet()比orElse()好(懒加载)- 调用
get()前必须用isPresent()判断
下一篇预告:序列化版本号 serialVersionUID 不写会怎样?—— 不写 JVM 会自动生成,但不同 JVM 实现可能不一样,反序列化会失败。