僵尸花园家庭防御
16.86MB · 2025-10-01
Java8发布都十几年了,但有个东西好像一直没被大家真正用起来——那就是Optional
。
是它不好用吗?还是大家不知道咋用?
每次看到代码里一大堆null
检查,我就想:这不就是Optional
该上场的时候吗?
可现实是,真正用Optional
的人还真不多。
为啥要有Optional
?
以前我们写Java,最头疼的就是NullPointerException(NPE)
。动不动就给你来个空指针,程序直接崩溃。
想想你是不是经常写这样的代码:
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
怎么实现上面的判断
Optional.ofNullable(getUserById(1))
.map(User::getAddress)
.map(Address::getCity)
.ifPresent(city -> System.out.println(city.toUpperCase()));
可以看到代码是即方便有简洁,但为啥大家还是不用?
这是最真实的答案。
老程序员:我写了十年Java,写if照样很顺畅?
新人:我学的时候老师也没重点讲Optional
啊,感觉像是可有可无。
结果就是:知道Optional,但不用。
离谱的用法:
Optional<User> opt = Optional.ofNullable(user);
if (opt.isPresent()) {
User u = opt.get();
// 干活
}
???
这不就是把if (user != null)
换了个马甲吗?
包装一下,再解包,多此一举。
这种用法,别说推广了,看了都想删库。
你在service
层返回Optional<User>
,隔壁老王接过去一脸懵:
Optional<User> result = userService.findUser(id);
// 然后呢?咋取值?咋判空?咋处理没找到的情况?
他可能直接.get()
,或者干脆 .orElse(null)
,又回到了原始社会。
没有共识,Optional 就成了团队里的异类,没人敢用,也不敢改。
比如,MyBatis
查询,如果没查到,默认返回null
。
SpringBoot接口,你返回 Optional<String>
,前端能接住吗?
不能。
HTTP返回的是JSON,Optional
是Java的类型,它不该出现在接口层。
很多人就是因为这点,觉得Optional
没啥用,所以干脆不用。
它的最佳战场,是方法内部的链式处理,和作为方法的返回值,表达可能不存在的语义。我们来看看正确的使用场景是怎么样的。
再看一遍这个:
String city = Optional.ofNullable(user)
.map(User::getAddress)
.map(Address::getCity)
.orElse("未知城市");
一行搞定,清晰明了。比嵌套 if 强得多多。
比如你写个方法,根据条件找用户:
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
的精髓。
以前你可能这么写:
String name = user.getName();
if (name == null) {
name = "匿名用户";
}
Optional
可以这样写:
String name = Optional.ofNullable(user.getName())
.orElse("匿名用户");
或者更狠点:
String name = Optional.ofNullable(user.getName())
.filter(n -> !n.trim().isEmpty())
.orElse("匿名用户");
加个filter
,连空字符串都干掉,一步到位。
public void saveUser(Optional<User> user) { ... }
谁这么写,建议面壁。调用方会疯:“我是该传null还是传Optional.empty()
?”
Optional 应该是返回值,不是入参。
public class User {
private Optional<String> email; // 不太对!
}
JPA、MyBatis、JSON 序列化全会出问题。Optional
不是持久化类型,不可往数据库里塞。
List<Optional<String>> names = ...; // 恶心
集合本身就可以为空或空集合,要是再套一层Optional
,直接套娃地狱了。
自己懂是非常好了,但要能是让团队一起用起来,那效果就更佳了。
定个规则:
比如:
findById
→ Optional<User>
findByEmail
→ Optional<User>
findFirstActive
→ Optional<Order>
这样调用方就知道:哦,得处理找不到的情况。
写个工具类,封装常用操作:
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);
}
}
看到嵌套 if 判空,可以评论:
“这块可以用Optional
链式调用优化下,提升可读性,要不要试试?”
别一上来就说“你这代码不优雅”,容易干架。
Optional
不只是map
和orElse
。
filter
:条件过滤Optional.ofNullable(user)
.filter(u -> u.getAge() >= 18)
.ifPresent(u -> System.out.println("成年用户:" + u.getName()));
比 if (user != null && user.getAge() >= 18)
干净多了。
flatMap
:避免 Optional 嵌套Optional<Optional<String>> nested = Optional.ofNullable(user)
.map(u -> Optional.ofNullable(u.getNickName()));
// 错的,嵌套了!
正确姿势:
Optional<String> name = Optional.ofNullable(user)
.flatMap(u -> Optional.ofNullable(u.getNickName()));
flatMap
会自动拍平一层Optional
。
orElseThrow
:找不到就抛异常User user = userService.findById(123)
.orElseThrow(() -> new UserNotFoundException("用户不存在"));
比 if (user == null) throw ...
更声明式,更语义化。
说了这么多好话,也得说点它的局限性。Optional
不是万能的。
null
问题(除非你每层都包装)。所以它只是一个工具,部分情况可以让你的代码写的更清晰、更安全。
场景 | 推荐用法 |
---|---|
链式取值 | 大力用,告别嵌套 if |
方法返回值 | 用于“可能为空”的查询方法 |
参数类型 | 不用 |
实体类字段 | 不用 |
集合元素 | 不用 |
接口返回 | 别直接返回 Optional 给前端 |
核心思想:
Optional
表达可能不存在的语义。null
判断。《工作 5 年没碰过分布式锁,是我太菜还是公司太稳?网友:太真实了!》
《别再被 Stream.toMap() 劝退了!3 个真实避坑案例,建议收藏》
《写给小公司前端的 UI 规范:别让页面丑得自己都看不下去》
《终于找到 Axios 最优雅的封装方式了,再也不用写重复代码了》