Java Optional 的使用
Optional 是 Java 8 引入的容器类,用于表示可能为 null 的值。理解 Optional 的使用可以避免 NullPointerException,使代码更加安全。本章将详细介绍 Java 中的 Optional。
Optional 创建与常用方法
创建 Optional
import java.util.Optional;
// 创建空的 Optional
Optional<String> empty = Optional.empty();
// 创建非空的 Optional
Optional<String> nonEmpty = Optional.of("Hello");
// 创建可能为 null 的 Optional
String value = null;
Optional<String> nullable = Optional.ofNullable(value); // Optional.empty()
String value2 = "Hello";
Optional<String> nullable2 = Optional.ofNullable(value2); // Optional["Hello"]
常用方法
Optional<String> optional = Optional.of("Hello");
// 判断是否有值
boolean isPresent = optional.isPresent(); // true
// 获取值(如果为空抛出异常)
String value = optional.get(); // "Hello"
// 如果为空返回默认值
String result = optional.orElse("默认值"); // "Hello"
String result2 = Optional.empty().orElse("默认值"); // "默认值"
// 如果为空使用 Supplier 提供默认值
String result3 = optional.orElseGet(() -> "默认值");
// 如果为空抛出异常
String result4 = optional.orElseThrow(); // 如果为空抛出 NoSuchElementException
String result5 = optional.orElseThrow(() -> new RuntimeException("值为空"));
避免 NullPointerException
传统方式的问题
// ❌ 传统方式:容易产生 NullPointerException
public String getUserName(User user) {
if (user != null) {
if (user.getName() != null) {
return user.getName().toUpperCase();
}
}
return "未知";
}
使用 Optional
// ✅ 使用 Optional:更安全
public String getUserName(User user) {
return Optional.ofNullable(user)
.map(User::getName)
.map(String::toUpperCase)
.orElse("未知");
}
实际示例
import java.util.Optional;
public class OptionalExample {
// 传统方式
public static String getCityTraditional(User user) {
if (user != null) {
Address address = user.getAddress();
if (address != null) {
return address.getCity();
}
}
return "未知城市";
}
// Optional 方式
public static String getCityOptional(User user) {
return Optional.ofNullable(user)
.map(User::getAddress)
.map(Address::getCity)
.orElse("未知城市");
}
}
class User {
private Address address;
public Address getAddress() { return address; }
}
class Address {
private String city;
public String getCity() { return city; }
}
示例:链式调用
示例 1:基本链式调用
Optional<String> optional = Optional.of("Hello");
// map:转换值
Optional<String> upper = optional.map(String::toUpperCase);
// 结果:Optional["HELLO"]
// filter:过滤
Optional<String> filtered = optional.filter(s -> s.length() > 3);
// 结果:Optional["Hello"]
// flatMap:扁平化
Optional<String> flatMapped = optional.flatMap(s -> Optional.of(s + " World"));
// 结果:Optional["Hello World"]
示例 2:复杂链式调用
public class OptionalChainExample {
public static String processUser(User user) {
return Optional.ofNullable(user)
.map(User::getAddress)
.map(Address::getCity)
.filter(city -> city.length() > 3)
.map(String::toUpperCase)
.orElse("默认城市");
}
public static void main(String[] args) {
User user = new User();
user.setAddress(new Address("北京"));
String city = processUser(user);
System.out.println(city); // "北京"
String city2 = processUser(null);
System.out.println(city2); // "默认城市"
}
}
示例 3:条件处理
Optional<String> optional = Optional.of("Hello");
// ifPresent:如果有值则执行
optional.ifPresent(System.out::println); // 输出:Hello
// ifPresentOrElse:如果有值执行一个操作,否则执行另一个
optional.ifPresentOrElse(
System.out::println, // 有值时执行
() -> System.out.println("值为空") // 空时执行
);
示例 4:组合多个 Optional
Optional<String> name = Optional.of("张三");
Optional<Integer> age = Optional.of(25);
// 组合两个 Optional
Optional<Person> person = name.flatMap(n ->
age.map(a -> new Person(n, a))
);
// 或使用辅助方法
Person person2 = name.flatMap(n ->
age.map(a -> new Person(n, a))
).orElse(new Person("未知", 0));
示例 5:集合中的 Optional
import java.util.*;
import java.util.stream.Collectors;
List<String> names = Arrays.asList("张三", "李四", null, "王五");
// 过滤 null 值
List<String> nonNull = names.stream()
.filter(Objects::nonNull)
.collect(Collectors.toList());
// 使用 Optional 处理
List<String> processed = names.stream()
.map(Optional::ofNullable)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
// 更简洁的方式
List<String> processed2 = names.stream()
.map(Optional::ofNullable)
.flatMap(opt -> opt.map(Stream::of).orElseGet(Stream::empty))
.collect(Collectors.toList());
Optional 的最佳实践
1. 不要将 Optional 作为字段
// ❌ 不推荐
public class User {
private Optional<String> name; // 不推荐
}
// ✅ 推荐
public class User {
private String name; // 可以为 null
}
2. 不要将 Optional 作为方法参数
// ❌ 不推荐
public void process(Optional<String> value) {
// ...
}
// ✅ 推荐
public void process(String value) {
Optional.ofNullable(value)
.ifPresent(this::doProcess);
}
3. 使用 Optional 作为返回值
// ✅ 推荐:表示可能为空
public Optional<User> findUserById(int id) {
// 可能返回 null
User user = userRepository.findById(id);
return Optional.ofNullable(user);
}
// 使用
Optional<User> user = findUserById(1);
user.ifPresent(u -> System.out.println(u.getName()));
4. 避免使用 get()
// ❌ 不推荐:可能抛出异常
String value = optional.get();
// ✅ 推荐:使用 orElse 或 orElseGet
String value = optional.orElse("默认值");
String value2 = optional.orElseGet(() -> "默认值");
5. 使用 map 和 flatMap
// ✅ 推荐:使用 map 转换
Optional<String> upper = optional.map(String::toUpperCase);
// ✅ 推荐:使用 flatMap 扁平化
Optional<String> result = optional.flatMap(s ->
Optional.of(s + " World")
);
实际示例
示例 1:用户服务
import java.util.Optional;
public class UserService {
public Optional<User> findUserById(int id) {
// 模拟数据库查询
if (id > 0) {
return Optional.of(new User(id, "张三"));
}
return Optional.empty();
}
public String getUserName(int id) {
return findUserById(id)
.map(User::getName)
.orElse("未知用户");
}
public void printUserInfo(int id) {
findUserById(id).ifPresent(user -> {
System.out.println("用户ID:" + user.getId());
System.out.println("用户名:" + user.getName());
});
}
}
class User {
private int id;
private String name;
public User(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() { return id; }
public String getName() { return name; }
}