【java基础】一篇文章彻底搞懂Optional

生活中,最使人疲惫的往往不是道路的遥远,而是心中的郁闷;最使人痛苦的往往不是生活的不幸,而是希望的破灭;最使人颓废的往往不是前途的坎坷,而是自信的丧失;最使人绝望的往往不是挫折的打击,而是心灵的死亡。所以我们要有自己的梦想,让梦想的星光指引着我们走出落漠,走出惆怅,带着我们走进自己的理想。

导读:本篇文章讲解 【java基础】一篇文章彻底搞懂Optional,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

基本说明

Optional对象是一种包装器对象,要么包装了类型T的对象,要么没有包装任何对象。对于第一种情况,我们称这种值是存在的。Optional类型被当作一种更安全的方式,用来替代类型T的引用,这种引用要么引用某个对象,要么为null。但是,它只有在正确使用的情况下才会更安全,在这篇文章中会进行介绍

注意:学习Optional,必须得要熟练掌握泛型和lambda表达式,前置知识还有Stream。如果不清楚,请参考泛型请参考 泛型程序设计基础 ,lambda请参考 一篇文章彻底搞懂lambda表达式,Stream请参考Stream流的各种操作

Optional类介绍

在java中,我们要学习一个类,第一步要做的应该就是去了解该类的继承关系以及有些上面方法,在有需要时还应当查看一些源代码中的注释。

下面我们就来看看Optional的类图以及该类中的方法和字段

在这里插入图片描述

我们再来看看该类上面的注释

在这里插入图片描述

A container object which may or may not contain a non-null value. If a value is present, isPresent() will return true and get() will return the value.
Additional methods that depend on the presence or absence of a contained value are provided, such as orElse() (return a default value if value not present) and ifPresent() (execute a block of code if the value is present).
This is a value-based class; use of identity-sensitive operations (including reference equality (==), identity hash code, or synchronization) on instances of Optional may have unpredictable results and should be avoided.

翻译过来就是

一个容器对象,它可能包含也可能不包含非null值。如果存在一个值,isPresent()将返回true,get()则返回该值。

提供了取决于所包含值是否存在的其他方法,如orElse()(如果值不存在,则返回默认值)和ifPresent()(当值存在时执行代码块)。

这是一个基于价值的类;在Optional实例上使用标识敏感操作(包括引用相等(==)、标识哈希代码或同步)可能会产生不可预测的结果,应避免。

Optional的创建

我们要创建Optional是通过该类的静态方法of或者ofNullable方法,通过empty方法可以产生一个为空的Optional

of方法创建

在这里插入图片描述

        // of方法中不能传入null,如果传入null,会提示空指针异常
        Optional<String> stringOptional = Optional.of("hello world");
        // NullPointerException
        Optional<Object> nullOption = Optional.of(null);

ofNullable方法创建

在这里插入图片描述

        // 和of方法一样的,只不过当传入的值为null时返回一个空的Optional
        Optional<String> stringOptional = Optional.ofNullable("hello");
        Optional<Object> nullOption = Optional.ofNullable(null);

empty方法创建

在这里插入图片描述

        Optional<Object> empty = Optional.empty();

获取Optional的值

这里介绍四个方法来获取Optional的值,分别是get,orElse,orElseGet,orElseThrow。它们都能获取Optional对象的值,区别就是Optional对象为空时的操作不一样

get方法

该方法可以获取Optional的值

在这里插入图片描述

        Optional<String> stringOptional = Optional.of("hello");
        Optional<Object> empty = Optional.empty();

        String s = stringOptional.get();
        System.out.println(s);
        Object o = empty.get();
        System.out.println(o);

上面代码的输出如下

在这里插入图片描述

当使用get获取为空的Optional时会报一个NoSuchElementException


orElse方法

该方法和get一样,只不过当Optional为空时会返回一个默认值

在这里插入图片描述

        Optional<String> stringOptional = Optional.of("hello");
        Optional<Object> empty = Optional.empty();

        String s = stringOptional.orElse("自己定义的默认值1");
        System.out.println(s);
        Object o = empty.orElse("自己定义的默认值2");
        System.out.println(o);

上面代码输出为

在这里插入图片描述


orElseGet方法

这个方法会在Optional为空时调用传入的lambda生成一个值

在这里插入图片描述

如果大家忘记了Supplier,请参考下面的源代码

在这里插入图片描述

        Optional<String> stringOptional = Optional.of("hello");
        Optional<Object> empty = Optional.empty();
        Supplier<String> supplier = () -> {
            System.out.println("\n----执行lambda----");
            return "default Value";
        };

        String s = stringOptional.orElseGet(supplier);
        System.out.println(s);
        Object o = empty.orElseGet(supplier);
        System.out.println(o);

上面的代码输出为

在这里插入图片描述


orElseThrow

经过上面三个方法的学习想必这个方法做什么的大家都能够猜出来了,就是当Optional为空时抛出一个自己传入的异常。注意:传入的是lambda表达式

在这里插入图片描述

        Optional<String> stringOptional = Optional.of("hello");
        Optional<Object> empty = Optional.empty();

        String s = stringOptional.orElseThrow(ArrayIndexOutOfBoundsException::new);
        System.out.println(s);
        Object o = empty.orElseThrow(ArithmeticException::new);
        System.out.println(o);

上面代码输出为

在这里插入图片描述


消费Optional值

在上一小节,我们看到了如何在不存在任何值的情况下产生相应的替代物。另一条使用可选值的策略是只有在其存在的情况下才消费该值。

ifPresent方法

ifPresent方法会接受一个函数。如果可选值存在,那么它会被传递给该函数。否则,不会发生任何事情。

在这里插入图片描述

我们需要传入一个Consumer,下面就是Consumer的源代码

在这里插入图片描述

        Optional<String> stringOptional = Optional.of("hello");
        Optional<Object> empty = Optional.empty();

        stringOptional.ifPresent(x -> System.out.println(1));
        empty.ifPresent(x -> System.out.println(2));

上面代码输出为

在这里插入图片描述

ifPresentOrElse

如果想要在可选值存在时执行一种动作,在可选值不存在时执行另一种动作,可以使用ifPresentOrElse。注意:该方法是java9新增的,java8不能使用

在这里插入图片描述

        Optional<String> stringOptional = Optional.of("hello");
        Optional<Object> empty = Optional.empty();
        
        // jdk 9
        stringOptional.ifPresentOrElse(System.out::println, () -> System.out.println("默认输出1"));
        stringOptional.ifPresentOrElse(System.out::println, () -> System.out.println("默认输出2"));

上面代码输出为
在这里插入图片描述


管道化Optional值

对于管道化Optional值,我们就直接简单理解Optional就是大小为0或者1得流,既然流里面有map,filter,当然在Optional中也可以进行相同的操作

map方法

map方法可以用来转换Optional内部的值

在这里插入图片描述

我们查看源代码,其实该方法的本质就是调用了ofNullable方法。Function接口内容如下

在这里插入图片描述

        Optional<String> stringOptional = Optional.of("hello");
        Optional<Object> empty = Optional.empty();

        Optional<String> newStrOptional = stringOptional.map(v -> "hello world");
        System.out.println(newStrOptional);
        Optional<String> newEmptyOptional = empty.map(v -> "hello world xxx");
        System.out.println(newEmptyOptional);

上面代码输出为

在这里插入图片描述


filter方法

这个方法听名字就知道是过滤的,使用该方法,如果满足条件或者为空,那么就返回当前对象本身,否则返回一个空Optional

在这里插入图片描述

Predicate接口内容如下

在这里插入图片描述

        Optional<String> stringOptional = Optional.of("hello");

        Optional<String> stringOptional1 = stringOptional.filter(v -> v.length() > 3);
        Optional<String> stringOptional2 = stringOptional.filter(v -> v.length() > 6);
        System.out.println(stringOptional1);
        System.out.println(stringOptional2);

上面代码输出为

在这里插入图片描述


or方法

调用这个方法,在Optional为空时会根据传入的lambda表达式生成一个Optional方法,注意:该方法需要jdk9+

在这里插入图片描述

        Optional<Object> empty = Optional.empty();
        // jdk9
        Optional<Object> or = empty.or(()->Optional.of("xxx"));
        
        System.out.println(empty);
        System.out.println(or);

上面代码输出为

在这里插入图片描述


flatMap的用法

大家如果学过Stream里面的flatMap,那么再来学习Optional里面的flatMap应该就容易很多了。

Stream里面的flatMap是将产生流的两个方法组合起来,其实现方式是摊平由流构成的流。如果将可选值解释为具有0个或1个元素,那么Optional.flatMap方法与其操作方式一样。

flatMap的源代码如下

在这里插入图片描述

现在假设你有一个可以产生Optional<T>对象的方法f,并且目标类型T具有一个可以产生Optional<T>对象的方法g。如果它们都是普通的方法,那么你可以通过调用s.f().g()来将它们组合起来。但是这种组合无法工作,因为s.f()的类型为Optional<T>,而不是T。因此,需要调用flatMap。

下面代码就是一个示例

        class B {
        }
        class A {
            public Optional<A> f() {
                return Optional.of(new A());
            }

            public Optional<B> g() {
                return Optional.of(new B());
            }
        }
        A a = new A();
        // error
        a.f().g();
        // ok
        Optional<B> optionalB = a.f().flatMap(A::g);

如果s.f()的值存在,那么g就可以应用到它上面。否则,就会返回一个空Optional<T>。很明显,如果有更多可以产生Optional值的方法或lambda表达式,那么就可以重复此过程。你可以直接将flatMap的调用链接起来,从而构建由这些步骤构成的管道,只有所有步骤都成功,该管道才会成功。


测试代码

下面就是本篇文章中所使用的代码,大家感兴趣可以自己运行测试一下

package com.ttpfx;

import org.junit.Test;

import java.util.Optional;
import java.util.function.Supplier;

/**
 * @author ttpfx
 * @date 2023/3/20
 */
public class OptionTest {

    public static void main(String[] args) {

    }


    @Test
    public void of() {
        // of方法中不能传入null,如果传入null,会提示空指针异常
        Optional<String> stringOptional = Optional.of("hello world");
        // NullPointerException
        Optional<Object> nullOption = Optional.of(null);
    }

    @Test
    public void ofNullable() {
        // 和of方法一样的,只不过当传入的值为null时返回一个空的Optional
        Optional<String> stringOptional = Optional.ofNullable("hello");
        Optional<Object> nullOption = Optional.ofNullable(null);
    }

    @Test
    public void empty() {
        Optional<Object> empty = Optional.empty();
    }

    @Test
    public void get() {
        Optional<String> stringOptional = Optional.of("hello");
        Optional<Object> empty = Optional.empty();

        String s = stringOptional.get();
        System.out.println(s);
        Object o = empty.get();
        System.out.println(o);
    }

    @Test
    public void orElse() {
        Optional<String> stringOptional = Optional.of("hello");
        Optional<Object> empty = Optional.empty();

        String s = stringOptional.orElse("自己定义的默认值1");
        System.out.println(s);
        Object o = empty.orElse("自己定义的默认值2");
        System.out.println(o);
    }

    @Test
    public void orElseGet() {
        Optional<String> stringOptional = Optional.of("hello");
        Optional<Object> empty = Optional.empty();
        Supplier<String> supplier = () -> {
            System.out.println("\n----执行lambda----");
            return "default Value";
        };

        String s = stringOptional.orElseGet(supplier);
        System.out.println(s);
        Object o = empty.orElseGet(supplier);
        System.out.println(o);
    }

    @Test
    public void orElseThrow() {
        Optional<String> stringOptional = Optional.of("hello");
        Optional<Object> empty = Optional.empty();

        String s = stringOptional.orElseThrow(ArrayIndexOutOfBoundsException::new);
        System.out.println(s);
        Object o = empty.orElseThrow(ArithmeticException::new);
        System.out.println(o);
    }

    @Test
    public void ifPresent() {
        Optional<String> stringOptional = Optional.of("hello");
        Optional<Object> empty = Optional.empty();

        stringOptional.ifPresent(x -> System.out.println(1));
        empty.ifPresent(x -> System.out.println(2));
    }

    // @Test
    // public void ifPresentOrElse() {
    //     Optional<String> stringOptional = Optional.of("hello");
    //     Optional<Object> empty = Optional.empty();
    //
    //     // jdk 9
    //     stringOptional.ifPresentOrElse(System.out::println, () -> System.out.println("默认输出1"));
    //     empty.ifPresentOrElse(System.out::println, () -> System.out.println("默认输出2"));
    // }

    @Test
    public void map() {
        Optional<String> stringOptional = Optional.of("hello");
        Optional<Object> empty = Optional.empty();

        Optional<String> newStrOptional = stringOptional.map(v -> "hello world");
        System.out.println(newStrOptional);
        Optional<String> newEmptyOptional = empty.map(v -> "hello world xxx");
        System.out.println(newEmptyOptional);
    }

    @Test
    public void filter() {
        Optional<String> stringOptional = Optional.of("hello");

        Optional<String> stringOptional1 = stringOptional.filter(v -> v.length() > 3);
        Optional<String> stringOptional2 = stringOptional.filter(v -> v.length() > 6);
        System.out.println(stringOptional1);
        System.out.println(stringOptional2);
    }

    // @Test
    // public void or() {
    //     Optional<Object> empty = Optional.empty();
    //     // jdk9
    //     Optional<Object> or = empty.or(()->Optional.of("xxx"));
    //
    //     System.out.println(empty);
    //     System.out.println(or);
    // }


    @Test
    public void flatMap() {
        class B {
        }
        class A {
            public Optional<A> f() {
                return Optional.of(new A());
            }

            public Optional<B> g() {
                return Optional.of(new B());
            }
        }
        A a = new A();
        // error
        // a.f().g();
        // ok
        Optional<B> optionalB = a.f().flatMap(A::g);
    }

    @Test
    public void optionalToStream() {
        Optional<String> stringOptional = Optional.of("hello");

    }
}


使用Optional的一些注意事项

下面是一些有关Optional类型正确用法的提示:

  • Optional类型的变量永远都不应该为null。
  • 不要使用Optional类型的域。因为其代价是额外多出来一个对象。在类的内部,使用null表示缺失的域更易于操作。
  • 不要在集合中放置Optional对象,并且不要将它们用作map的键。应该直接收集其中的值。

总结

对于Optional,我们简单理解为就是对某种类型的封装即可。要么为该对象本身,要么为null。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/140181.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!