Java 8 Optional:告别 NullPointerException 的优雅姿势
Java 8 Optional:告别 NullPointerException 的优雅姿势
NullPointerException(NPE)大概是每个 Java 程序员都踩过的坑。在 Java 8 之前,我们处理可能为 null 的值时,只能写一堆 if (obj != null) 的判断,代码里到处都是防御性检查,既不美观也容易遗漏。Java 8 引入的 Optional 就是为了解决这个问题而生的。
Optional 是什么?
Optional 是一个容器类,它可以保存类型 T 的值,也可以保存 null。它的核心思想是:用类型系统显式地表达"这个值可能不存在",而不是靠文档注释或者约定。
创建 Optional
// 创建一个包含值的 Optional
Optional<String> opt1 = Optional.of("Hello");
// 创建一个可能为空的 Optional(允许传 null)
Optional<String> opt2 = Optional.ofNullable(null);
// 创建一个空的 Optional
Optional<String> opt3 = Optional.empty();
注意:Optional.of() 如果传入 null 会直接抛 NPE,所以不确定是否为空时,用 ofNullable() 更安全。
常用操作
1. 判断与取值
Optional<String> opt = Optional.ofNullable(getName());
// 传统写法
if (opt.isPresent()) {
System.out.println(opt.get());
}
// 推荐写法:orElse 提供默认值
String name = opt.orElse("未知用户");
// 或者抛出自定义异常
String name2 = opt.orElseThrow(() -> new RuntimeException("名字不能为空"));
2. map 与 flatMap
这是 Optional 最强大的地方,可以链式调用,避免层层 null 判断:
// 传统写法
String city = null;
if (user != null) {
Address address = user.getAddress();
if (address != null) {
city = address.getCity();
}
}
// Optional 写法
String city = Optional.ofNullable(user)
.map(User::getAddress)
.map(Address::getCity)
.orElse("未知城市");
一行搞定,清爽多了。
3. filter 过滤
Optional<String> result = Optional.ofNullable(email)
.filter(e -> e.contains("@"))
.map(String::toLowerCase);
如果邮箱不包含 @,结果就是空的 Optional。
常见误区
1. 把 Optional 当作字段类型
// 不推荐
class User {
private Optional<String> name;
}
Optional 的设计初衷是作为方法返回值,而不是类的字段。它没有实现 Serializable 接口,序列化会有问题,而且会增加内存开销。
2. 用 get() 代替 orElse()
// 危险!如果为空会抛 NoSuchElementException
String name = opt.get();
// 推荐
String name = opt.orElse("默认值");
get() 本质上只是把 NPE 换成了另一种异常,没有解决问题。
3. 过度使用
// 没必要
Optional.ofNullable(name).ifPresent(n -> System.out.println(n));
// 直接写更清晰
if (name != null) {
System.out.println(name);
}
Optional 适合用在链式调用和多层嵌套的场景,简单的 null 判断没必要强行用。
实战示例
假设有一个查询用户的方法,可能返回 null:
public Optional<User> findUserById(Long id) {
User user = userMapper.selectById(id);
return Optional.ofNullable(user);
}
// 调用方
String email = findUserById(1L)
.map(User::getEmail)
.filter(e -> e.endsWith("@example.com"))
.orElseThrow(() -> new BusinessException("未找到有效邮箱"));
调用方一眼就能看出这个方法可能返回空值,必须处理,而不是等到运行时才炸。
一句话总结
- Optional 是方法返回值的"空值包装器",不是字段类型。
- 优先用
map、flatMap、orElse做链式操作,少用isPresent()+get()。 - 它能帮你减少 NPE,但不能完全替代 null 判断,简单场景别过度使用。
- 点赞
- 收藏
- 关注作者
评论(0)