Optional
NullPointerException——Java 史上最常见的异常。
你一定见过这样的代码:
java
public String getCity(User user) {
if (user != null) {
if (user.getAddress() != null) {
if (user.getAddress().getCity() != null) {
return user.getAddress().getCity().getName();
}
}
}
return "未知";
}嵌套 if,绕来绕去。Optional 让这件事变得优雅。
创建 Optional
三种创建方式
java
import java.util.Optional;
// 方式一:空值
Optional<String> empty = Optional.empty();
// 方式二:非空值(传入 null 会抛异常)
Optional<String> present = Optional.of("Hello");
// 方式三:可为 null(推荐大多数场景)
Optional<String> nullable = Optional.ofNullable(getName());
Optional<String> nullable2 = Optional.ofNullable(null); // 返回空 Optional什么时候用哪种
| 方法 | 适用场景 |
|---|---|
Optional.empty() | 明确返回空值 |
Optional.of(value) | 确定值不为 null |
Optional.ofNullable(value) | 不确定值是否可能为 null |
判断与取值
判断值是否存在
java
Optional<String> opt = Optional.ofNullable(getName());
// JDK 8+
if (opt.isPresent()) {
String value = opt.get(); // 安全取值
}
// JDK 11+:更语义化
if (opt.isEmpty()) {
System.out.println("值为空");
}获取值
java
Optional<String> opt = Optional.ofNullable(getName());
// get():直接取值,空值抛异常
String value = opt.get();
// orElse():空值时返回默认值
String value = opt.orElse("未知");
// orElseGet():空值时用 Supplier 计算
String value = opt.orElseGet(() -> {
// 只在 opt 为空时才会执行
return computeDefault();
});
// orElseThrow():空值时抛自定义异常
String value = opt.orElseThrow(() ->
new IllegalArgumentException("名称不能为空"));orElse vs orElseGet
java
Optional<String> opt = Optional.empty();
// orElse:无论是否为空都执行
String a = opt.orElse(getDefault()); // ❌ getDefault() 总是被执行
// orElseGet:只在为空时执行
String b = opt.orElseGet(() -> getDefault()); // ✅ 只在 opt 为空时才执行
// 对比
private static String getDefault() {
System.out.println("计算默认值"); // 这个方法会被调用吗?
return "default";
}链式操作
Optional 的真正威力在于链式调用。
map() — 转换
java
Optional<String> name = Optional.of("hello");
// 把 String 转成 Integer(长度)
Optional<Integer> length = name.map(String::length);
length.ifPresent(System.out::println); // 5
// 嵌套属性安全获取
String city = Optional.ofNullable(user)
.map(User::getAddress)
.map(Address::getCity)
.map(City::getName)
.orElse("未知");flatMap() — 避免嵌套 Optional
java
// map 的问题:返回值已经是 Optional 时
Optional<Optional<String>> nested = opt.map(this::findById);
// findById 返回 Optional<String>
// flatMap 解决:自动展开一层
Optional<String> result = opt.flatMap(this::findById);filter() — 过滤
java
Optional<Integer> age = Optional.of(25);
// 保留满足条件的值
Optional<Integer> filtered = age.filter(a -> a > 18);
filtered.ifPresent(System.out::println); // 25
// 不满足条件,返回空 Optional
Optional<Integer> filtered2 = age.filter(a -> a > 100);
filtered2.isEmpty(); // true完整示例
替代嵌套 if
java
// ❌ 传统方式
public String getCityName(User user) {
if (user != null) {
Address address = user.getAddress();
if (address != null) {
City city = address.getCity();
if (city != null) {
return city.getName();
}
}
}
return "未知";
}
// ✅ Optional 方式
public String getCityName(User user) {
return Optional.ofNullable(user)
.map(User::getAddress)
.map(Address::getCity)
.map(City::getName)
.orElse("未知");
}方法返回值
java
// ❌ 返回 null
public User findById(Long id) {
return database.query(id);
}
// ✅ 返回 Optional
public Optional<User> findById(Long id) {
return Optional.ofNullable(database.query(id));
}
// 调用方
user.findById(1L)
.ifPresent(user -> System.out.println("找到: " + user.getName()));集合操作
java
List<Optional<String>> list = Arrays.asList(
Optional.of("a"),
Optional.empty(),
Optional.of("c")
);
// 过滤掉空值,收集非空值
List<String> result = list.stream()
.flatMap(Optional::stream)
.toList();
// [a, c]
// JDK 9+:直接用 Optional.ofNullable
List<String> nullableList = Arrays.asList("a", null, "c");
List<String> nonNull = nullableList.stream()
.flatMap(s -> Optional.ofNullable(s).stream())
.toList();条件执行
java
Optional<String> config = getConfig();
// ❌ 传统方式
if (config.isPresent()) {
process(config.get());
}
// ✅ ifPresent
config.ifPresent(this::process);
// ✅ ifPresentOrElse:处理两种情况
config.ifPresentOrElse(
this::process,
() -> System.out.println("配置为空")
);注意事项
不要过度使用
java
// ❌ 过度使用:简单场景用 Optional 反而麻烦
String value = Optional.ofNullable(getValue())
.map(Object::toString)
.orElse(null);
// ✅ 简单场景直接判断
String value = getValue();
if (value != null) {
// ...
}不要用作字段类型
java
// ❌ Optional 不能序列化
public class User {
private Optional<String> name; // ❌ 不好
}
// ✅ 简单做法
public class User {
private String name;
public Optional<String> getName() {
return Optional.ofNullable(name);
}
}不要用作参数
java
// ❌ 不推荐
public void process(Optional<String> input) {
input.ifPresent(this::doProcess);
}
// ✅ 推荐
public void process(String input) {
String safeInput = Optional.ofNullable(input).orElse("");
doProcess(safeInput);
}小结
| 方法 | 说明 |
|---|---|
Optional.empty() | 创建空 Optional |
Optional.of(value) | 创建非空 Optional(null 会抛异常) |
Optional.ofNullable(value) | 安全创建,可空 |
isPresent() / isEmpty() | 判断是否存在 |
get() | 获取值(空值抛异常) |
orElse() / orElseGet() / orElseThrow() | 获取或默认值 |
ifPresent() / ifPresentOrElse() | 消费值 |
map() / flatMap() | 转换值 |
filter() | 过滤值 |
记住:Optional 是为了链式安全地处理 null,不是为了替代所有 null 判断。
