Java8学习笔记 -- Optional类

在开发过程中,空指针异常是最常见,不过也是比较容易修改的。尽管如此,为了避免空指针,我们可能会加入大量的检测逻辑。好在Java8中为我们提供了Optional类,它拥有一整套完善的为空检测及处理逻辑,大大的方便了我们的开发。

Optional类的Javadoc

Optional类实际上就是一个容器,里面保存着我们的对象,并提供取方法,并且可以为存为null的对象。

创建一个Optional对象:

1.Optional.of(obj)

这个方法需要传入一个非空的对象

2.Optional.ofNullable(obj)

这个不同于上面的方法,它允许传入一个为空的对象

3.Optional.empty()

严格意义来说,这个并不算,他是返回一个空的Optional实例,注意,并不是为null的实例,而是不包含其他对象的实例。源码如下:

private static final Optional<?> EMPTY = new Optional<>();

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

具体使用

获得了一个Optional实例后,关键就是怎么好好使用。首先这个类提供了一个为空判断方法:isPresent(),但是千万不要单纯的使用这个方法,比如:

private void test(Person person){
        Optional<Person> optional = Optional.ofNullable(person);
        if(optional.isPresent()){
            //...
            return;
        }
        //...
    }

那简直是对这个类的一大浪费,而且又无故多写了许多代码。我们可以看一下isPresent()的实现:

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

这不就是我们经常写的为空判断么。而且大致看一眼这个类的方法,也能感觉到它不仅仅只是做一个判断这么简单。接下来,我们就一起来学习一下其他那些非常有用的方法,之后你也许就会感受到设计人员的良苦用心了。

1.get()

首先我们来看看取对象的方法,在以此为扩展了解其他方法。Optional既然是一个容器,那么肯定能存能取,不然要他干嘛?

这个方法语义很清晰,就是取之前我们保存的对象,当对象为空时,抛出空指针异常。可能有人要问了,既然只是简单的取值,而且还是会抛异常,哪有何用?对,这个方法本身并没啥用,就是一个简单的get方法,除非你配合isPresent(),但那样又回到了以前的思维,所以我们主要来看后面的方法。

2.orElse(T other)

这个就比较使用了,他要求传入一个默认值,当我们之前保存的值为空时,就返回传入的默认值,否则返回之前保存的值。

public static void main(String[] args){
        test(null); //10
        test(new Person(15)); //15
    }

    public static void test(Person person){
        Optional<Person> optional = Optional.ofNullable(person);
        out.println(optional.orElse(new Person(10)).getAge());
    }

帮我们省略了一些if-else逻辑,是不是很人性化?不要急着满足,后面还有更精彩的。

3.orElseGet(Supplier<? extends T> other)

这个与上面类似,只不过为空时不再返回一个默认值,而是通过提供的方法,返回这个方法所返回的值:

public static void main(String[] args){
        test(null); //10
        test(new Person(15)); //15
    }

public static void test(Person person){
        Optional<Person> optional = Optional.ofNullable(person);
        out.println(optional.orElseGet(()->new Person(10)).getAge());
    }

(为了简洁,我一般都用lambda表达式形式书写代码,如果对lambda表达式不熟悉的,可以参考我的这篇文章)

4.ifPresent(Consumer<? super T> consumer)

一般来说,我们进行为空判断后肯定要接着执行一些操作。所以,设计人员为我们提供了这个方法,基本功能就是如果我们所存的对象不为空时,执行所指定的代码,为空时什么都不做:

public static void test(Person person){
        Optional<Person> optional = Optional.ofNullable(person);
        optional.ifPresent(p -> out.println(p.getAge()));
    }

这样一来为空判断和后续逻辑就连贯在一起了。

5.map(Function<? super T,? extends U> mapper)

有时候,我们从某个不为空的对象上使用某些方法后,也会生成空对象,因此就要要连续多级连续判断,放在以前,就要大量if-else嵌套,而map函数很方便这些操作:

optional.map(p -> p.getName())
        .map(n -> n.toUpperCase())
        .orElse("ABC")

换成等价的if-else代码

if (person != null) {
       String name = person.getName();
       if (name != null) {
            out.println(name.toUpperCase());
        }else{
            out.println("ABC");
        }
}else{
        out.println("ABC");
}

map和ifPresent类似,也是在不为空时执行指定逻辑,只不过map返回的也是一个Optional,所以map链可以无限延长,代替了if嵌套if的语句,不仅简单而且结构清晰。如果某一环为空,则不会执行所指定的逻辑,返回一个空的Optional实例。所以我们可以在某一环添加一些其他的方法,进行为空时的逻辑处理。类似的自由组合可以创造无限可能。

6.filter(Predicate<? super T> predicate)

了解过流式编程的朋友看到Optional的这些方法可能会很熟悉(如果不熟悉也没关系,在下一篇文章中学习过Stream类后,你就会很了解这类编程风格),这些方法应该是这一类API的标准方法,所以怎能少的了filter?

顾名思义,这个方法就是过滤的意思,只有符合我们所指定的逻辑后,才会向下传递,否者传递一个空的Optional,当然,身为空指针检测类,他的前提条件当然是不为空,否则不会执行传入的代码:

optional.filter(p -> p.getAge()>18).orElse(new Person(19, "jack")).getAge()

这个就很方便我们在不为空的同时进行逻辑判断,同时由于filter也是返回一个Optional对象,所以也可以搭配其他方法使用。

7.flatMap(Function<? super T,Optional<U>> mapper)

这个方法名字中也有一个map,那么应该可map有关联。对的,他的功能和map差不多,都是类似转换功能,但是,map的返回值是自动帮我们包装好的Optional对象,而flatmap这需要我们自己包装一个Optional对象:

optional.flatMap(p -> Optional.ofNullable(p.getName()))

可见flatMap灵活性较map大一点,不过map既然这样设计也是方便我们开发,不一定每次为空检测后,都要返回一个不相干的类型,所以如何取舍,还是自己决定。ψ(*`ー´)ψ

8.orElseThrow(Supplier<? extends X> exceptionSupplier)

Optional类的方法并不多,到这里我们已经讲得差不多了,但是是不是觉得少点什么呢?原来前面的方法基本上都是检测为空时,直接不执行了,这样虽然不会报错,但是对于我们后期维护检测问题并不好。

Optional.of虽然会抛异常,但是他是在一开始包装的时候就抛,如果这个对象不为空,对象的成员为空呢?总不能还要用get()方法吧,get虽然可以,但只是抛一个传统的空指针异常,如果要抛自定义异常呢?

这时候就要用orElseThrow了,他在为空时会抛一个我们指定的异常,由于传递的是代码块,所以我们也可以在抛异常前,执行一些其他逻辑:

optional.orElseThrow(()->{out.println("hello");return new Throwable("abc");});

小结

这个类主要功能就是空指针检测,只不过设计人员为我们添加了许多实用的扩展方法,节省了我们大量代码,总体来说是很不错的。有兴趣的朋友可以看一看这个类是怎么实现的,由于是相对独立的,所以理解起来难度并不高。而且这个类的方法都是比较简单地,基础好的朋友也许自己都能创造出类似的类。

总之,既然为我们提供了这么实用的工具,我们就要在以后的实践中尽量有意识的多用用,不要再像以前一样if…else… ๑乛乛๑

Java8学习笔记目录

    原文作者:留给时光吧
    原文地址: https://www.jianshu.com/p/94c69d2ef00f
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞