4. Optional

4.1 概述

我们在编写代码的时候出现最多的就是空指针异常。所以在很多情况下我们需要做各种非空的判断。

例如:

        Author author = getAuthor();
        if(author!=null){
            System.out.println(author.getName());
        }

尤其是对象中的属性还是一个对象的情况下。这种判断会更多。

而过多的判断语句会让我们的代码显得臃肿不堪。

所以在JDK8中引入了Optional,养成使用Optional的习惯后你可以写出更优雅的代码来避免空指针异常。

并且在很多函数式编程相关的API中也都用到了Optional,如果不会使用Optional也会对函数式编程的学习造成影响。

4.2 使用

4.2.1 创建对象

Optional就好像是包装类,可以把我们的具体数据封装Optional对象内部。然后我们去使用Optional中封装好的方法操作封装进去的数据就可以非常优雅的避免空指针异常。

我们一般使用Optional的静态方法ofNullable来把数据封装成一个Optional对象。无论传入的参数是否为null都不会出现问题。

        Author author = getAuthor();
        Optional<Author> authorOptional = Optional.ofNullable(author);

ofNullable 方法:创建一个包含指定值的 Optional 实例,如果指定值为 null,则创建一个空的 Optional。

Optional<String> optional = Optional.ofNullable(null);

isPresent 方法:判断 Optional 中是否包含值。

if (optional.isPresent()) {
    // 值存在的情况下的操作
    String value = optional.get();
} else {
    // 值不存在的情况下的操作
}

你可能会觉得还要加一行代码来封装数据比较麻烦。但是如果改造下getAuthor方法,让其的返回值就是封装好的Optional的话,我们在使用时就会方便很多。

而且在实际开发中我们的数据很多是从数据库获取的。Mybatis从3.5版本可以也已经支持Optional了。我们可以直接把dao方法的返回值类型定义成Optional类型,MyBastis会自己把数据封装成Optional对象返回。封装的过程也不需要我们自己操作。

如果你确定一个对象不是空的则可以使用Optional的静态方法of来把数据封装成Optional对象。

        Author author = new Author();
        Optional<Author> authorOptional = Optional.of(author);

但是一定要注意,如果使用of的时候传入的参数必须不为null。(尝试下传入null会出现什么结果)

如果一个方法的返回值类型是Optional类型。而如果我们经判断发现某次计算得到的返回值为null,这个时候就需要把null封装成Optional对象返回。这时则可以使用Optional的静态方法empty来进行封装。

		Optional.empty()

所以最后你觉得哪种方式会更方便呢?ofNullable

4.2.2 安全消费值

我们获取到一个Optional对象后肯定需要对其中的数据进行使用。这时候我们可以使用其ifPresent方法对来消费其中的值。

这个方法会判断其内封装的数据是否为空,不为空时才会执行具体的消费代码。这样使用起来就更加安全了。

例如,以下写法就优雅的避免了空指针异常。

        Optional<Author> authorOptional = Optional.ofNullable(getAuthor());

        authorOptional.ifPresent(author -> System.out.println(author.getName()));

4.2.3 获取值

如果我们想获取值自己进行处理可以使用get方法获取,但是不推荐。因为当Optional内部的数据为空的时候会出现异常。

get 方法是 Optional 类中的一个方法,用于获取 Optional 对象中的值。如果 Optional 对象包含一个非空的值,get 方法返回该值,否则抛出 NoSuchElementException 异常。

Optional<String> optional = Optional.of("Hello, World!");

// 获取Optional对象中的值
String value = optional.get();
System.out.println(value);  // 输出: Hello, World!

在上述例子中,Optional 包含一个非空值,因此调用 get 方法会返回该值。

然而,需要注意的是,使用 get 方法之前最好先使用 isPresent 方法检查 Optional 中是否包含值。这是因为如果 Optional 中没有值,调用 get 方法将会抛出 NoSuchElementException 异常,这可能导致程序中断。为了避免这种情况,通常建议使用 isPresent 方法或者其他方法来避免直接调用 get

Optional<String> optional = Optional.of("Hello, World!");

// 在调用get之前检查是否有值
if (optional.isPresent()) {
    String value = optional.get();
    System.out.println(value);  // 输出: Hello, World!
} else {
    System.out.println("Optional is empty");
}

除了 get 方法外,Optional 还提供了其他更安全的方法,如 orElseorElseGet,它们允许在 Optional 不包含值时提供默认值,而不会抛出异常。

4.2.4 安全获取值

如果我们期望安全的获取值。我们不推荐使用get方法,而是使用Optional提供的以下方法。

  • orElseGet

    获取数据并且设置数据为空时的默认值。如果数据不为空就能获取到该数据。如果为空则根据你传入的参数来创建对象作为默认值返回。

        Optional<Author> authorOptional = Optional.ofNullable(getAuthor());
        Author author1 = authorOptional.orElseGet(() -> new Author());

  • orElseThrow

    获取数据,如果数据不为空就能获取到该数据。如果为空则根据你传入的参数来创建异常抛出。

        Optional<Author> authorOptional = Optional.ofNullable(getAuthor());
        try {
            Author author = authorOptional.orElseThrow((Supplier<Throwable>) () -> new RuntimeException("author为空"));
            System.out.println(author.getName());
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }

4.2.5 过滤

我们可以使用filter方法对数据进行过滤。如果原本是有数据的,但是不符合判断,也会变成一个无数据的Optional对象。

Optional<Author> authorOptional = Optional.ofNullable(getAuthor());
authorOptional.filter(author -> author.getAge()>100).ifPresent(author -> System.out.println(author.getName()));

4.2.6 判断

我们可以使用isPresent方法进行是否存在数据的判断。如果为空返回值为false,如果不为空,返回值为true。但是这种方式并不能体现Optional的好处,更推荐使用ifPresent方法。

        Optional<Author> authorOptional = Optional.ofNullable(getAuthor());

        if (authorOptional.isPresent()) {
            System.out.println(authorOptional.get().getName());
        }

ifPresentOptional 类中的一个方法,用于在 Optional 包含值的情况下执行指定的操作。该方法接收一个 Consumer 函数式接口,如果 Optional 包含非空值,则会调用该函数来处理该值。

Optional<String> optional = Optional.of("Hello, World!");

// 如果Optional包含值,就执行指定的操作
optional.ifPresent(value -> System.out.println("Value: " + value));

在上述例子中,ifPresent 方法接收一个 Consumer,这个 Consumer 是一个函数接口,它接收一个参数并执行一些操作。在这里,我们使用 Lambda 表达式作为 Consumer,输出包含在 Optional 中的值。

这种使用方式更为安全,因为它避免了直接调用 get 方法可能引发的 NoSuchElementException 异常。在使用 Optional 的时候,推荐优先使用 ifPresent 方法,而不是直接调用 get

另外,ifPresent 还可以与其他 Optional 方法链式调用,使代码更为简洁。例如:

Optional<String> optional = Optional.of("Hello, World!");

optional
    .map(String::toUpperCase)
    .ifPresent(upperCaseValue -> System.out.println("Upper Case Value: " + upperCaseValue));

在这个例子中,map 方法将 Optional 中的值转换为大写形式,然后 ifPresent 方法判断是否有值,有值则执行输出操作。

4.2.7 数据转换

Optional还提供了map可以让我们的对数据进行转换,并且转换得到的数据也还是被Optional包装好的,保证了我们的使用安全。

例如我们想获取作家的书籍集合。

    private static void testMap() {
        Optional<Author> authorOptional = getAuthorOptional();
        Optional<List<Book>> optionalBooks = authorOptional.map(author -> author.getBooks());
        optionalBooks.ifPresent(books -> System.out.println(books));
    }

总结:

Optional 是 Java 8 引入的一个类,主要用于解决空指针异常(NullPointerException)的问题。它是一个容器对象,可以包含或不包含非空值。使用 Optional 的目的是明确表示一个值是否存在,并提供了一些方法来更安全地进行值的操作。

以下是 Optional 的一些主要方法和用法:

  1. 创建 Optional 实例

    • Optional.of(T value): 创建包含非空值的 Optional 实例,如果值为 null,则抛出 NullPointerException
    Optional<String> optional = Optional.of("Hello, World!");
    
    • Optional.ofNullable(T value): 创建包含指定值的 Optional 实例,如果值为 null,则创建一个空的 Optional
    Optional<String> optional = Optional.ofNullable(null);
    
    • Optional.empty(): 创建一个空的 Optional 实例。
    Optional<String> emptyOptional = Optional.empty();
    
  2. 判断是否包含值

    • isPresent(): 如果 Optional 包含非空值,则返回 true
    if (optional.isPresent()) {
        // 包含值的情况下的操作
    } else {
        // 不包含值的情况下的操作
    }
    
  3. 获取值

    • get(): 获取 Optional 中的值,如果值为空,抛出 NoSuchElementException 异常。在使用之前最好使用 isPresent() 进行检查。
    String value = optional.get();
    
  4. 处理值

    • ifPresent(Consumer consumer): 如果 Optional 包含值,就执行指定的操作。
    optional.ifPresent(value -> System.out.println("Value: " + value));
    
    • orElse(T other): 如果 Optional 包含值,就返回该值;否则返回提供的默认值。
    String result = optional.orElse("Default Value");
    
    • orElseGet(Supplier supplier): 如果 Optional 包含值,就返回该值;否则根据提供的 Supplier 函数生成一个值。
    String result = optional.orElseGet(() -> generateDefaultValue());
    
    • orElseThrow(Supplier exceptionSupplier): 如果 Optional 包含值,就返回该值;否则抛出由提供的 Supplier 生成的异常。
    String result = optional.orElseThrow(() -> new NoSuchElementException("No value present"));
    
  5. 映射值

    • map(Function mapper): 如果 Optional 包含值,则对该值执行提供的映射函数。
    Optional<String> upperCaseOptional = optional.map(String::toUpperCase);
    
    • flatMap(Function> mapper): 如果 Optional 包含值,则对该值执行提供的映射函数,并返回结果。
    Optional<String> upperCaseOptional = optional.flatMap(value -> Optional.ofNullable(value.toUpperCase()));
    

Optional 的使用有助于编写更健壮、更安全的代码,尤其是在处理可能为 null 的情况下。然而,要谨慎使用 get() 方法,最好配合其他方法如 isPresent()orElse()ifPresent() 等来确保安全地处理 Optional 中的值。

你可能感兴趣的:(各种计算机相关小知识,大数据学习,java学习,java,数据库,开发语言,算法,leetcode,排序算法,数据结构)