Java Optional (看完就会)

什么是 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 可以使代码更加简洁和易于维护。

  • 13
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值