欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java:Optional详解(源码阅读与应用)

程序员文章站 2022-03-04 11:01:44
...

前言

Optional对象应该来说在实际应用中还是很广泛的,最近写了篇文章讲解java.util.function包中的几个常见类:Consumer、Supplier、Predicate、Function等的用法,然后举例时也用Optional对象进行了举例,可能有些小伙伴对它的用法还不是很了解,所以我今天就单独列一篇文章出来讲讲

我们来看一下Optional的源码,或者点此直达正文

public final class Optional<T> {
 
    private static final Optional<?> EMPTY = new Optional<>();

    private final T value;

    private Optional() {
        this.value = null;
    }

    public static<T> Optional<T> empty() {
        @SuppressWarnings("unchecked")
        Optional<T> t = (Optional<T>) EMPTY;
        return t;
    }

    private Optional(T value) {
        this.value = Objects.requireNonNull(value);
    }

    public static <T> Optional<T> of(T value) {
        return new Optional<>(value);
    }

    public static <T> Optional<T> ofNullable(T value) {
        return value == null ? empty() : of(value);
    }

    public T get() {
        if (value == null) {
            throw new NoSuchElementException("No value present");
        }
        return value;
    }

    public boolean isPresent() {
        return value != null;
    }

    public void ifPresent(Consumer<? super T> consumer) {
        if (value != null)
            consumer.accept(value);
    }

    public Optional<T> filter(Predicate<? super T> predicate) {
        Objects.requireNonNull(predicate);
        if (!isPresent())
            return this;
        else
            return predicate.test(value) ? this : empty();
    }

    public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Optional.ofNullable(mapper.apply(value));
        }
    }

    public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Objects.requireNonNull(mapper.apply(value));
        }
    }

    public T orElse(T other) {
        return value != null ? value : other;
    }

    public T orElseGet(Supplier<? extends T> other) {
        return value != null ? value : other.get();
    }

    public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
        if (value != null) {
            return value;
        } else {
            throw exceptionSupplier.get();
        }
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }

        if (!(obj instanceof Optional)) {
            return false;
        }

        Optional<?> other = (Optional<?>) obj;
        return Objects.equals(value, other.value);
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(value);
    }

    @Override
    public String toString() {
        return value != null
            ? String.format("Optional[%s]", value)
            : "Optional.empty";
    }
}

其实常用的方法也是那几个,我们拿几个常用的方法举几个例子

一、of()和ofNullable()

看一下源码,他俩就是用来构建Optional对象的,我们看一下Optional对象有两个私有构造方法,一个无参的,一个有参的,重点是有参的这个

(有参)构造函数

private Optional(T value) {
    this.value = Objects.requireNonNull(value);
}

他在构建的时候会检查value值,如果为null的话,会报空指针异常

public static <T> T requireNonNull(T obj) {
    if (obj == null)
        throw new NullPointerException();
    return obj;
}

我们先来看of()方法的定义

public static <T> Optional<T> of(T value) {
    return new Optional<>(value);
}

发现它调用的是有参的构造方法,如果参数为null,则会抛出空指针异常

在实际使用的时候,如果我们直接传个null值进来,IDEA会给予提示让我们替换成ofNullable()方法

我们来看一下ofNullable()方法的定义

public static <T> Optional<T> ofNullable(T value) {
    return value == null ? empty() : of(value);
}

发现它多了一步判断,如果值为null时,则返回一个empty()对象,不为null才调用of()方法进行构建

我们来看一下empty()方法到底返回了个啥
Java:Optional详解(源码阅读与应用)

Api文档上表明,此方法返回一个空的Optional实例,这个Optional实例中没有任何值,方法里的泛形:<T>也比较有意思,文档上说的是:一种不存在的值

我们来调用empty()方法试试

@Test
public void test() {
    Optional<Object> empty = Optional.empty();
    System.out.println(empty.get());
}

发现调用empty.get()方法后报错了,抛了NoSuchElementException异常

java.util.NoSuchElementException: No value present

看了get()方法的定义我们就明白了,当值为空时,get()方法会抛出NoSuchElementException异常,就是因为empty()方法返回的就是Optional对象第一行里定义的EMPTY全局常量,而EMPTY的值是new的一个Optional对象,调用的正是Optional对象的无参私有方法,无参私有方法里value的值正是null,所以调用get方法时会抛异常

public T get() {
    if (value == null) {
        throw new NoSuchElementException("No value present");
    }
    return value;
}

现在我们来总结一下区别

方法 总结
of() 适合构建知道参数一定不为null的情况,如果参数为null,则在创建对象的时候就会抛出空指针异常
ofNullable() 适合构建参数可能为null的情况,如果参数为null,调用Optional.get()方法时会抛出NoSuchElementException异常

二、isPresent()和ifPresent()

isPresent()比较简单,就是用来判断值是否为null的,比如我们可以在调用get方法前用isPresent()判断一下值是否为空,不为空的时候我们再调用get方法

ifPresent()方法用的比较多,我们来看一下方法定义

public void ifPresent(Consumer<? super T> consumer) {
    if (value != null)
        consumer.accept(value);
}

可以看到它接收的是一个Consumer对象,结合前一篇文章中对Consumer的了解,我们举个例子看一下

@Test
public void test() {
	// 从getList方法中取值,getList方法可能返回null,而ifPresent的意思是,如果值不为空,
	// 则调用Consumer对象进行消费,刚好Consumer中的accept方法接收一个参数,并且只负责消费,没有返回值
    Optional.ofNullable(this.getList(0)).ifPresent(list -> {
        System.out.println(list.size());
    });
}
private List<String> getList(int i) {
    return i == 0 ? null : Arrays.asList("jack", "rose", "zhangsan", "wangwu");
}

三、filter()

从名称应该也能猜到,它是用来做过滤的,我们来看一下定义

public Optional<T> filter(Predicate<? super T> predicate) {
    Objects.requireNonNull(predicate);
    if (!isPresent())
        return this;
    else
        return predicate.test(value) ? this : empty();
}

我们看到它接收的是一个Predicate类型参数,并且参数不能为空,如果结果满足定义的Predicate表达式,则返回本身,如果不满足,则返回一个空Optional对象

我们来写个例子看一下

@Test
public void test() {
    List<String> nameList = Arrays.asList("jack", "rose", "zhangsan", "wangwu");
    // nameList满足过滤条件,则返回对象本身
    Optional<List<String>> opt = Optional.of(nameList).filter(list -> list.contains("rose") && list.contains("jack"));
    System.out.println(opt.orElse(null));
    // [jack, rose, zhangsan, wangwu]
}

三、map()和flatMap()

如果我们熟悉Stream.map()的用法,我们应该对此不陌生,map就是用来作转换的,我们可以看一下方法定义,它接收的是一个Function类型的参数,我们挨个看一下

先看map()方法的定义

public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
    Objects.requireNonNull(mapper);
    if (!isPresent())
        return empty();
    else {
        return Optional.ofNullable(mapper.apply(value));
    }
}

从定义中可以看到,它接受一个不能为空的Function类型参数,如果value为空,则返回空Optional对象,如果参数不为空,则先调用Function类型参数进行转换,最后再用Optional.ofNullable()方法包裹并返回

我们来看看用法

@Test
public void test() {
    List<String> nameList = Arrays.asList("jack", "rose", "zhangsan", "wangwu");
    Optional<String> opt = Optional.of(nameList).map(list -> "name: " + StringUtils.join(list, ','));
    System.out.println(opt.orElse(null));
    // name: jack,rose,zhangsan,wangwu
}

可以看到,map方法传入一个Function类型参数,泛形参数<? super T, ? extends U>中,T代表原始类型,U代表要转换成的类型,flatMap()方法最大的不同是泛形参数定义为<? super T, Optional<U>,可以看到,转换后的类型必须为一个Optional对象,而且从源码中可以看到,该Optional对象不能为空,否则会抛空指针异常

还是用map的方法,我们来改成flatMap试试

@Test
public void test() {
    List<String> nameList = Arrays.asList("jack", "rose", "zhangsan", "wangwu");
    Optional<String> opt = Optional.of(nameList).flatMap(list -> Optional.of("name: " + StringUtils.join(list, ',')));
    System.out.println(opt.orElse(null));
}

这样一看二者的区别就很明显了

四、orElse()和orElseGet()

orElse()比较简单,就是当Optional对象的值为空时,返回orElse(xx)里定义的参数

orElseGet()方法有点不一样,它的入参个是Supplier类型的参数,这个平时用得多一点,我们改造上面flatMap用到的例子看一下

@Test
public void test() {
	// 故意让getList方法返回null
    Optional<String> opt = Optional.ofNullable(this.getList(0)).flatMap(list -> Optional.of("name: " + StringUtils.join(list, ',')));
    // 然后orElseGet()方法接收一个Supplier,返回长度为10位的随机字符串
    System.out.println(opt.orElseGet(() -> RandomStringUtils.randomAlphabetic(10)));
    // UlFAPMLRVw
}
private List<String> getList(int i) {
    return i == 0 ? null : Arrays.asList("jack", "rose", "zhangsan", "wangwu");
}

从例子中可以看出,如果我们不确认get的结果会不会为null时,可以使用orElseGet()方法保证再返回一个备用参数

五、orElseThrow()

顾名思义,这个方法是抛异常的,举个例子看一下

@Test
public void test() {
	// 故意让getList方法返回null,当value为null时,触发抛异常的操作
    List<String> list = Optional.ofNullable(this.getList(0)).orElseThrow(() -> new RuntimeException("系统异常"));
    System.out.println(list);
}

运行后,发现抛了异常:java.lang.RuntimeException: 系统异常


到此,我们基本上把Optional对象中的方法过了一遍,相信照着例子多敲几遍多用几次后大家一定很对它各个方法的使用场景会很熟悉