Java8发布都十几年了,但有个东西好像一直没被大家真正用起来------那就是Optional
。 是它不好用吗?还是大家不知道咋用?
每次看到代码里一大堆null
检查,我就想:这不就是Optional
该上场的时候吗? 可现实是,真正用Optional
的人还真不多。
前言
为啥要有Optional
?
以前我们写Java,最头疼的就是NullPointerException(NPE)
。动不动就给你来个空指针,程序直接崩溃。
想想你是不是经常写这样的代码:
java
User user = getUserById(1);
if (user != null) {
Address address = user.getAddress();
if (address != null) {
String city = address.getCity();
if (city != null) {
System.out.println(city.toUpperCase());
}
}
}
这种"瀑布式"的null
检查,写起来烦,看起来乱,一不小心就漏掉一个判空,然后运行时给你来个NullPointerException
。
Optional
的本意就是让开发者显式地处理可能为null
的情况,把运行时的空指针异常转化为编译时的约束。
来看看用Optional
怎么实现上面的判断
java
Optional.ofNullable(getUserById(1))
.map(User::getAddress)
.map(Address::getCity)
.ifPresent(city -> System.out.println(city.toUpperCase()));
可以看到代码是即方便有简洁,但为啥大家还是不用?
一、为什么没人用?
1. 已经习惯了 if
这是最真实的答案。 老程序员:我写了十年Java,写if照样很顺畅? 新人:我学的时候老师也没重点讲Optional
啊,感觉像是可有可无。
结果就是:知道Optional,但不用。
2. 用错了,反而更乱
离谱的用法:
java
Optional<User> opt = Optional.ofNullable(user);
if (opt.isPresent()) {
User u = opt.get();
// 干活
}
??? 这不就是把if (user != null)
换了个马甲吗? 包装一下,再解包,多此一举。 这种用法,别说推广了,看了都想删库。
3. 团队不统一,风格乱套
你在service
层返回Optional<User>
,隔壁老王接过去一脸懵:
java
Optional<User> result = userService.findUser(id);
// 然后呢?咋取值?咋判空?咋处理没找到的情况?
他可能直接.get()
,或者干脆 .orElse(null)
,又回到了原始社会。
没有共识,Optional 就成了团队里的异类,没人敢用,也不敢改。
4. 和 Spring、MyBatis 配合得不好?
比如,MyBatis
查询,如果没查到,默认返回null
。 SpringBoot接口,你返回 Optional<String>
,前端能接住吗?
不能。 HTTP返回的是JSON,Optional
是Java的类型,它不该出现在接口层 。 很多人就是因为这点,觉得Optional
没啥用,所以干脆不用。
二、Optional 到底该用在哪?
它的最佳战场,是方法内部的链式处理 ,和作为方法的返回值,表达可能不存在的语义。我们来看看正确的使用场景是怎么样的。
场景 1:链式取值
再看一遍这个:
java
String city = Optional.ofNullable(user)
.map(User::getAddress)
.map(Address::getCity)
.orElse("未知城市");
一行搞定,清晰明了。比嵌套 if 强得多多。
场景 2:方法返回可能为空的结果
比如你写个方法,根据条件找用户:
java
public Optional<User> findActiveUserByEmail(String email) {
User user = userRepo.findByEmail(email);
if (user != null && user.isActive()) {
return Optional.of(user);
}
return Optional.empty();
}
调用方一看返回类型是Optional<User>
,就知道:"哦,可能没有,得处理"。 而不是盲猜:"这个方法会不会返回null?文档在哪?"
类型即文档 ,这才是Optional
的精髓。
场景 3:避免 null 的默认值处理
以前你可能这么写:
java
String name = user.getName();
if (name == null) {
name = "匿名用户";
}
Optional
可以这样写:
java
String name = Optional.ofNullable(user.getName())
.orElse("匿名用户");
或者更狠点:
java
String name = Optional.ofNullable(user.getName())
.filter(n -> !n.trim().isEmpty())
.orElse("匿名用户");
加个filter
,连空字符串都干掉,一步到位。
三、哪些地方不能用 Optional?
1. 别用作参数类型
java
public void saveUser(Optional<User> user) { ... }
谁这么写,建议面壁。调用方会疯:"我是该传null还是传Optional.empty()
?"
Optional 应该是返回值,不是入参。
2. 别在实体类里用
java
public class User {
private Optional<String> email; // 不太对!
}
JPA、MyBatis、JSON 序列化全会出问题。Optional
不是持久化类型,不可往数据库里塞。
3. 别在集合里用 Optional
java
List<Optional<String>> names = ...; // 恶心
集合本身就可以为空或空集合,要是再套一层Optional
,直接套娃地狱了。
四、怎么让团队接受 Optional?
自己懂是非常好了,但要能是让团队一起用起来,那效果就更佳了。
1. 先统一"返回值规范"
定个规则:
在查询方法里,增加如果可能查不到时,返回
Optional<T>
。
比如:
findById
→Optional<User>
findByEmail
→Optional<User>
findFirstActive
→Optional<Order>
这样调用方就知道:哦,得处理找不到的情况。
2. 提供工具方法,降低使用门槛
写个工具类,封装常用操作:
java
public class Optionals {
public static <T> T orDefault(Optional<T> opt, T def) {
return opt.orElse(def);
}
public static <T> Optional<T> ofNullabe(T t) {
return Optional.ofNullable(t);
}
}
3. 代码审查时,可以温和引导
看到嵌套 if 判空,可以评论: "这块可以用Optional
链式调用优化下,提升可读性,要不要试试?" 别一上来就说"你这代码不优雅",容易干架。
五、Optional 的进阶玩法
Optional
不只是map
和orElse
。
1. filter
:条件过滤
java
Optional.ofNullable(user)
.filter(u -> u.getAge() >= 18)
.ifPresent(u -> System.out.println("成年用户:" + u.getName()));
比 if (user != null && user.getAge() >= 18)
干净多了。
2. flatMap
:避免 Optional 嵌套
java
Optional<Optional<String>> nested = Optional.ofNullable(user)
.map(u -> Optional.ofNullable(u.getNickName()));
// 错的,嵌套了!
正确姿势:
java
Optional<String> name = Optional.ofNullable(user)
.flatMap(u -> Optional.ofNullable(u.getNickName()));
flatMap
会自动拍平一层Optional
。
3. orElseThrow
:找不到就抛异常
java
User user = userService.findById(123)
.orElseThrow(() -> new UserNotFoundException("用户不存在"));
比 if (user == null) throw ...
更声明式,更语义化。
六、Optional 的局限性
说了这么多好话,也得说点它的局限性。Optional
不是万能的。
- 它解决不了"深层嵌套对象"的
null
问题(除非你每层都包装)。 - 它对性能有轻微影响(对象创建开销)。
- 它不能跨网络传输(RPC、JSON)。
- 它需要团队共识,否则就是代码不规整。
所以它只是一个工具,部分情况可以让你的代码写的更清晰、更安全。
总结
场景 | 推荐用法 |
---|---|
链式取值 | 大力用,告别嵌套 if |
方法返回值 | 用于"可能为空"的查询方法 |
参数类型 | 不用 |
实体类字段 | 不用 |
集合元素 | 不用 |
接口返回 | 别直接返回 Optional 给前端 |
核心思想:
- 用
Optional
表达可能不存在的语义。 - 用链式调用简化
null
判断。 - 不滥用,不乱用。
公众号:程序员刘大华,专注分享前后端开发的实战笔记。关注我,少走弯路,一起进步!
📌往期精彩
《工作 5 年没碰过分布式锁,是我太菜还是公司太稳?网友:太真实了!》
《别再被 Stream.toMap() 劝退了!3 个真实避坑案例,建议收藏》