Optional处理空指针,这几招你会了吗?
# Optional处理空指针,这几招你会了吗?
作为一个Java开发者,谁没被空指针坑过呢?以前我们总得写一堆if(xxx != null)
,代码又臭又长。自从Java 8出了Optional,用好了真的能少写很多防御代码。今天就和大家聊聊怎么把这玩意儿用顺手了。
# 一、创建Optional的正确姿势
ofNullable() 并非魔法,它只是将 null 值封装到 Optional 对象中。真正的价值在于后续链式方法带来的安全操作空间。
# 1. 别踩坑的of方法
// 千万别这么写!传null直接爆炸
Optional<String> opt = Optional.of(null);
2
这个of()
方法就像个暴脾气的老哥,只要你敢传null,它立马甩你一个空指针异常。只有在100%确定对象不为null的时候才能用它。
# 2. 最常用的ofNullable
// 这才是正确打开方式
Optional<User> userOpt = Optional.ofNullable(userService.findById(123));
2
不管拿到的user是不是null,这个方法都能稳稳接住。相当于给可能为null的值套了个保护壳。
# 二、安全取值的几种姿势
# 1. 保底值大法:orElse
// 有值拿值,没值拿默认
User user = userOpt.orElse(new User("匿名用户"));
2
适合默认对象创建成本不高的场景。比如这里new一个默认用户,内存开销不大。
# 2. 延迟加载:orElseGet
// 只有需要时才创建默认用户
User user = userOpt.orElseGet(() -> {
// 从数据库加载默认配置
return defaultUserRepository.findByName("guest");
});
2
3
4
5
当默认对象构造比较费资源时(比如查数据库),一定要用这个!避免无谓的性能消耗。
# 3. 抛异常终结者
// 找不到就报错
Config config = configOpt.orElseThrow(() -> {
throw new ServiceException("系统配置缺失");
});
2
3
4
把空值转换为业务异常,比直接抛NPE有意义多了。比如这里明确告诉调用方是配置缺失的问题。主要是这种书写方式真的太优雅了
# 三、链式操作真香警告
# 1. 安全转换:map
// 三级安全转换
String city = userOpt
.map(User::getAddress) // 用户→地址
.map(Address::getCity) // 地址→城市
.orElse("未知地区"); // 保底值
2
3
4
5
不管哪个环节为null,都会自动跳转到保底值。相当于给每个get方法都加了空判断!
# 2. 避免套娃:flatMap
// 用户可能有地址,地址可能有坐标
Optional<Coordinate> coord = userOpt
.flatMap(user -> user.getAddress()) // 返回Optional<Address>
.flatMap(address -> address.getCoord());
2
3
4
当你的get方法本身就返回Optional时,用flatMap就不会出现Optional<Optional<...>>
这种套娃结构了。
# 四、这几个坑千万别踩
# 1. 不要用isPresent()+get()
// 错误示范!
if(userOpt.isPresent()){
User user = userOpt.get();
// 一堆操作
}
2
3
4
5
这写法跟直接判null没区别,完全没发挥Optional的优势。应该用orElse系列方法替代。
# 2. 别用在参数和字段上
// 方法参数不要这样写
public void badExample(Optional<String> param){...}
// 类字段也别用
class User {
private Optional<String> name; // 大忌!
}
2
3
4
5
6
7
Optional是设计用来做返回值的,滥用反而会让代码变复杂。
# 3. 空集合别用Optional
// 返回空集合更直观
public List<Order> findOrders(){
return Collections.emptyList();
}
// 比下面这种写法好
public Optional<List<Order>> findOrders(){...}
2
3
4
5
6
7
- 空集合 本身可以很好地表达“没有数据”的含义,无需画蛇添足地用 Optional 包装。
- 与 JPA 数据库的 API 设计思想一致,JPA 在查询不到数据时,通常返回空集合,而不是 null 或 Optional。
# 五、实际开发中的小技巧
# 1. 和Stream配合使用
// 过滤掉空值
List<String> names = users.stream()
.map(user -> Optional.ofNullable(user.getName()))
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
2
3
4
5
6
不过更推荐这样写:
List<String> names = users.stream()
.map(User::getName)
.filter(Objects::nonNull)
.collect(Collectors.toList());
2
3
4
# 2. 记录日志更方便
userOpt.ifPresent(user -> {
log.info("用户操作:{}", user.getName());
auditService.record(user);
});
2
3
4
这种写法既安全又直观,不需要先判空再操作。
# 六、到底该不该用Optional?
用了两年Optional的老司机说句实在话:
该用的时候大胆用,但别强行用。适合的场景:
- 明确可能返回null的方法返回值
- 需要链式处理可能为null的对象时
不适合的场景:
- 集合类(用空集合更好)
- 简单的非空判断(直接if判断更直观)
- 性能敏感的场景(Optional有对象创建开销)
最后记住:Optional是帮我们更优雅地处理null,不是用来消灭null的。关键还是要在写代码时对可能为null的地方保持敏感,结合业务场景选择最合适的处理方式。