Optional 深度解析
Optional 是 Java 8 引入的空值处理工具,正确使用可以优雅地避免 NullPointerException。
一、为什么需要 Optional
1.1 空值问题
// 之前:嵌套 if 检查
public String getCity(User user) {
if (user != null) {
Address address = user.getAddress();
if (address != null) {
String city = address.getCity();
if (city != null) {
return city;
}
}
}
return "Unknown";
}
// Optional:链式调用
public String getCity(User user) {
return Optional.ofNullable(user)
.map(User::getAddress)
.map(Address::getCity)
.orElse("Unknown");
}
1.2 设计初衷
Optional 不是为了替代 null
↓
而是提供一种更好的方式来处理可能为空的值
↓
让空值检查更明确、更安全
二、创建 Optional
2.1 of() - 非空值
// 值不能为 null
Optional<String> opt = Optional.of("Hello");
// ❌ 会抛 NPE
Optional<String> opt = Optional.of(null); // NullPointerException
2.2 ofNullable() - 可为空
// 值可为 null
String value = getValue();
Optional<String> opt = Optional.ofNullable(value);
// 等价于
Optional<String> opt = value != null ? Optional.of(value) : Optional.empty();
2.3 empty() - 空 Optional
Optional<String> opt = Optional.empty();
// 单例模式
private static final Optional<Object> EMPTY = Optional.empty();
2.4 工厂方法返回 Optional
// 推荐:返回 Optional 而不是 null
public Optional<User> findById(Long id) {
User user = dao.findById(id);
return Optional.ofNullable(user);
}
// 使用
Optional<User> userOpt = service.findById(1L);
三、获取值
3.1 get() - 直接获取
Optional<String> opt = Optional.of("Hello");
String value = opt.get(); // "Hello"
// ❌ 危险:可能抛异常
Optional<String> empty = Optional.empty();
String value = empty.get(); // NoSuchElementException
// ✅ 先检查
if (opt.isPresent()) {
String value = opt.get();
}
3.2 orElse() - 默认值
Optional<String> opt = Optional.ofNullable(getValue());
// 有值返回值,无值返回默认值
String result = opt.orElse("Default");
// 注意:默认值总是被计算
String result = opt.orElse(expensiveOperation()); // 即使有值也会执行
3.3 orElseGet() - 延迟计算
// 推荐:默认值延迟计算
String result = opt.orElseGet(() -> expensiveOperation());
// 只有当 Optional 为空时才执行
3.4 orElseThrow() - 抛异常
// 抛默认异常
String result = opt.orElseThrow(); // NoSuchElementException
// 抛自定义异常
String result = opt.orElseThrow(() ->
new BusinessException("USER_NOT_FOUND")
);
四、转换操作
4.1 map() - 值转换
Optional<User> userOpt = Optional.of(new User("Alice", 20));
// 转换值
Optional<String> nameOpt = userOpt.map(User::getName);
// 链式调用
Optional<String> cityOpt = userOpt
.map(User::getAddress)
.map(Address::getCity);
// 如果中间为 null,返回 empty
4.2 flatMap() - 扁平化
// map 返回嵌套 Optional
Optional<User> userOpt = Optional.of(new User());
Optional<Optional<String>> nested = userOpt.map(User::getCity);
// flatMap 扁平化
Optional<String> cityOpt = userOpt.flatMap(User::getCity);
// 场景:方法返回 Optional 时用 flatMap
public Optional<String> getCity(User user) {
return Optional.ofNullable(user.getCity());
}
Optional<String> result = userOpt.flatMap(this::getCity);
4.3 filter() - 过滤
Optional<Integer> opt = Optional.of(10);
// 满足条件保留,否则返回 empty
Optional<Integer> result = opt.filter(n -> n > 5); // [10]
Optional<Integer> empty = opt.filter(n -> n > 20); // empty
五、判断操作
5.1 isPresent() - 是否有值
Optional<String> opt = Optional.of("Hello");
if (opt.isPresent()) {
String value = opt.get();
System.out.println(value);
}
5.2 isEmpty() - 是否为空(Java 11)
if (opt.isEmpty()) {
System.out.println("No value present");
}
5.3 ifPresent() - 有值则执行
Optional<String> opt = Optional.of("Hello");
opt.ifPresent(value -> System.out.println(value));
// 方法引用
opt.ifPresent(System.out::println);
5.4 ifPresentOrElse() - Java 9
opt.ifPresentOrElse(
value -> System.out.println("Value: " + value),
() -> System.out.println("No value")
);
六、实战场景
6.1 数据库查询
// Repository
public Optional<User> findById(Long id) {
return Optional.ofNullable(userMapper.selectById(id));
}
// Service
public String getUserName(Long id) {
return userRepository.findById(id)
.map(User::getName)
.orElse("Unknown");
}
// 或者抛异常
public User getUser(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException(id));
}
6.2 配置读取
// 读取配置
public String getConfig(String key) {
return Optional.ofNullable(configMap.get(key))
.orElseGet(() -> loadDefaultConfig(key));
}
// 带类型转换
public Integer getIntConfig(String key) {
return Optional.ofNullable(configMap.get(key))
.map(Integer::parseInt)
.orElse(100);
}
6.3 链式调用
public String getUserCity(User user) {
return Optional.ofNullable(user)
.map(User::getAddress)
.flatMap(Optional::ofNullable)
.map(Address::getCity)
.orElse("Unknown");
}
6.4 集合处理
List<User> users = getUsers();
// 获取所有非空城市
List<String> cities = users.stream()
.map(User::getAddress)
.filter(Objects::nonNull)
.map(Address::getCity)
.filter(Objects::nonNull)
.collect(Collectors.toList());
// 使用 Optional
List<String> cities = users.stream()
.map(u -> Optional.ofNullable(u.getAddress())
.map(Address::getCity)
.orElse(null))
.filter(Objects::nonNull)
.collect(Collectors.toList());
6.5 方法参数
// ❌ 不推荐:Optional 作为参数
public void process(Optional<String> opt) {
// ...
}
// ✅ 推荐:方法重载
public void process(String value) {
process(Optional.ofNullable(value));
}
private void process(Optional<String> opt) {
// 内部处理
}
七、最佳实践
7.1 推荐用法
// ✅ 作为返回值
public Optional<User> findById(Long id);
// ✅ 链式调用
Optional.ofNullable(user)
.map(User::getName)
.orElse("Unknown");
// ✅ 延迟计算
opt.orElseGet(() -> expensiveOperation());
// ✅ 抛异常
opt.orElseThrow(() -> new BusinessException("NOT_FOUND"));
7.2 避免用法
// ❌ 调用 get() 前不检查
String value = opt.get(); // 可能抛异常
// ❌ 作为字段
public class User {
private Optional<String> name; // 不推荐
}
// ❌ 作为方法参数
public void process(Optional<String> opt) { // 不推荐
}
// ❌ 与 null 比较
if (opt == null) { // 错误
}
// ❌ 过度使用
Optional.ofNullable(value)
.map(v -> Optional.ofNullable(v.trim()))
.orElse(Optional.empty()); // 太复杂
7.3 性能考虑
// ❌ 创建过多 Optional 对象
Optional.ofNullable(a)
.map(v -> Optional.ofNullable(b))
.orElse(Optional.empty());
// ✅ 扁平化
Optional.ofNullable(a)
.flatMap(v -> Optional.ofNullable(b));
八、总结
Optional 核心要点:
| 操作 | 方法 | 说明 |
|---|---|---|
| 创建 | of, ofNullable, empty | 根据场景选择 |
| 获取 | get, orElse, orElseGet, orElseThrow | 优先 orElseGet |
| 转换 | map, flatMap, filter | 链式调用 |
| 判断 | isPresent, isEmpty, ifPresent | 避免直接 get |
Optional 是处理空值的利器,但不是银弹,正确使用才能发挥价值。