什么是 Optional 类?
Java 8 引入的 Optional 类是一个用于处理可选值的容器对象,旨在避免 NullPointerException 并提高代码的可读性和健壮性。Optional 类属于 java.util 包,可以包含一个非空值或仅仅包含 null。Optional
类提供了多种方法,使得开发者无需显式地进行空值检查,从而提高代码的可读性和安全性。
Optional 类的用法
创建 Optional 对象
创建 Optional
对象的方法有:
Optional.of(T value)
:创建一个非空的Optional
实例,传入的值不能为 null。Optional.empty()
:创建一个空的Optional
实例。Optional.ofNullable(T value)
:创建一个可以为 null 的Optional
实例。
public void createOptional() {
// 创建一个空的 Optional
Optional<Object> emptyOptional = Optional.empty();
// 创建一个非空的 Optional
Student student = new Student("李四", 20);
Optional<Student> studentOptional = Optional.of(student);
// 创建一个可接受 null 的 Optional
Student nullableStudent = null;
Optional<Student> nullableOptional = Optional.ofNullable(nullableStudent);
}
判断 Optional 容器中是否包含对象
使用以下方法判断 Optional
是否包含值:
boolean isPresent()
:判断Optional
中是否有值。void ifPresent(Consumer<? super T> consumer)
:如果有值,则执行传入的Consumer
函数。
public void checkOptional() {
Student student = new Student("张三", 22);
Optional<Student> studentOptional = Optional.ofNullable(student);
// 判断是否存在值
if (studentOptional.isPresent()) {
System.out.println("Student exists.");
}
// 使用 ifPresent 方法
studentOptional.ifPresent(s -> s.setName("王五"));
}
studentOptional.ifPresent(s -> s.setName("王五"))
: 这行代码使用ifPresent
方法,它接受一个Consumer
函数。如果studentOptional
包含值(即student
对象),它会执行Consumer
函数,将student
对象作为参数传入。在这个例子中,Consumer
函数将student
对象的name
属性修改为 "王五"。
获取 Optional 容器的对象
可以通过以下方法获取 Optional
中的值:
T get()
:如果值存在,返回该值;如果不存在,则抛出异常。T orElse(T other)
:如果有值,则返回该值;否则返回指定的默认值。T orElseGet(Supplier<? extends T> other)
:如果有值,则返回该值;否则返回由Supplier
提供的值。T orElseThrow(Supplier<? extends X> exceptionSupplier)
:如果有值,则返回该值;否则抛出由Supplier
提供的异常。
public void getOptionalValue() throws Exception {
Student student = null;
Optional<Student> studentOptional = Optional.ofNullable(student);
// 使用 get 方法
try {
Student retrievedStudent = studentOptional.get();
} catch (NoSuchElementException e) {
System.out.println("No student found.");
}
// 使用 orElse 方法
Student defaultStudent = studentOptional.orElse(new Student("默认学生", 18));
// 使用 orElseGet 方法
Student anotherDefaultStudent = studentOptional.orElseGet(() -> new Student("备用学生", 18));
// 使用 orElseThrow 方法
studentOptional.orElseThrow(() -> new Exception("学生信息缺失"));
}
过滤操作
filter
方法允许你根据特定条件检查 Optional
中的值是否存在。它接受一个 Predicate
作为参数。如果值存在且满足该条件,则返回包含该值的 Optional
;否则返回一个空的 Optional
。
Optional<T> filter(Predicate<? super T> predicate)
:如果值存在且满足给定的条件,返回该值的 Optional
;否则返回空的 Optional
。
Optional<String> optionalValue = Optional.of("Hello");
// 过滤 Optional,检查值是否以 "H" 开头
Optional<String> filteredValue = optionalValue.filter(value -> value.startsWith("H"));
// filteredValue 将包含 "Hello"
Optional<String> emptyValue = optionalValue.filter(value -> value.startsWith("A"));
// emptyValue 将是空的,因为 "Hello" 并不以 "A" 开头
映射操作
map
方法用于转换 Optional
中的值(如果存在)。它接受一个 Function
作为参数,定义如何转换该值。如果 Optional
包含一个值,则应用该函数,并返回一个包含转换后值的新 Optional
;如果 Optional
为空,则返回一个空的 Optional
。
<U> Optional<U> map(Function<? super T, ? extends U> mapper)
:如果值存在,则应用给定的映射函数,并返回映射后的 Optional
;否则返回空的 Optional
。
Optional<String> optionalValue = Optional.of("Hello");
// 将值映射到其长度
Optional<Integer> lengthValue = optionalValue.map(String::length);
// lengthValue 将包含 5,即 "Hello" 的长度
Optional<String> emptyValue = Optional.empty();
Optional<Integer> emptyLengthValue = emptyValue.map(String::length);
// emptyLengthValue 将是空的,因为原始的 Optional 是空的
扁平映射操作
flatMap
方法与 map
类似,但用于当映射函数本身返回一个 Optional
时。这在你想避免嵌套的 Optional
实例时非常有用。如果原始的 Optional
为空,flatMap
也会返回一个空的 Optional
;如果包含一个值,则应用映射函数并返回结果的 Optional
。
<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper)
:与 map
类似,但返回的结果是一个 Optional
。
Optional<String> optionalValue = Optional.of("Hello");
// 扁平映射,将值转换为其大写形式的 Optional
Optional<String> upperValue = optionalValue.flatMap(value -> Optional.of(value.toUpperCase()));
// upperValue 将包含 "HELLO"
Optional<String> emptyValue = Optional.empty();
Optional<String> emptyUpperValue = emptyValue.flatMap(value -> Optional.of(value.toUpperCase()));
// emptyUpperValue 将是空的,因为原始的 Optional 是空的
使用场景
Optional
在实际开发中非常有用,以下是一些典型的应用场景:
场景一:简化空值检查
在处理数据库查询结果时,常常需要检查返回的对象是否为 null。使用 Optional
可以有效简化这一过程。
Student student =
studentDao.getById(order.getStudentId());
Optional.ofNullable(student).ifPresent(s ->
response.setStudentName(s.getName()));
在这个场景中,我们从数据库中获取学生信息。
getById
方法可能返回 null。使用Optional.ofNullable(student)
可以有效地处理这种情况。如果student
不为 null,ifPresent
方法将执行,并将学生的姓名设置到响应对象中。这种方式避免了显式的 null 检查。
场景二:抛出异常
在某些情况下,程序需要确保某个对象的属性不为空,如果为空则需要抛出异常。Optional
可以帮助我们优雅地处理这种情况。
Student student = new Student(null, 20);
Optional.ofNullable(student)
.filter(s -> !isEmpty(s.getName()))
.orElseThrow(() -> new Exception("学生姓名不能为空"));;
在这个示例中,我们创建了一个
Student
对象,但其姓名为 null。使用Optional.ofNullable(student)
创建一个Optional
对象,并通过filter
方法检查学生的姓名是否为空。如果为空,orElseThrow
方法将抛出一个自定义异常,提示“学生姓名不能为空”。这种方式避免了显式的 null 检查和异常处理,使代码更加简洁。。
场景三:链式调用
Optional
的链式调用特性使得在处理嵌套对象时更加方便,避免了多层 null 检查。
public static String getStudentGrade(Student student) {
return Optional.ofNullable(student)
.map(Student::getGrade)
.map(Grade::getLetter)
.orElseThrow(() -> new IllegalArgumentException("学生信息无效"));
}
在这个场景中,我们需要获取学生的成绩字母。
Student
对象可能为 null,且getGrade()
方法也可能返回 null。使用Optional
后,我们可以将这些检查合并为一行代码。每个map
方法调用都会检查前一个对象是否为 null。如果任一对象为 null,则抛出异常。这样,代码更加简洁,逻辑清晰。
场景四:默认值处理
在某些情况下,我们希望在获取值时提供一个默认值,以避免 null 的影响。
int age = Optional.ofNullable(student)
.map(Student::getAge)
.orElse(18); // 默认年龄为 18
在这个示例中,我们从
student
对象中获取年龄。使用Optional.ofNullable(student)
创建一个可选对象。如果student
不为 null,我们通过map
方法获取年龄;如果student
为 null,则orElse
方法返回默认值 18。这样,我们避免了显式的 null 检查,并且代码更加简洁。
总结
Optional
类为 Java 提供了一种优雅的方式来处理可能为 null 的值。通过使用 Optional
,开发者可以减少空指针异常的发生,提高代码的可读性和安全性。在实际开发中,合理使用 Optional
可以使代码更加简洁和易于维护。