Java8 中的 Optional 类的基本使用

Java8 引入了一个十分有趣的 Optional 类它主要是为了解决臭名昭著的空指针异常(NullPointerException)。当咱们对对象的属性进行检查,判断它的值是否为指望的格式,最终却发现咱们查看的并非一个对象,而是一个空指针,它会当即抛出一个让人厌烦的 NullPointerException 异常。java

抛砖

咱们来看一个简单的实例:函数

String address = world.getCountry.getCity.getName;
复制代码

在获得地址以前,须要对各个类进行检查防止出现空指针异常:spa

public String getAddress(World world){
        if (world != null){
            Country country = world.getCountry();
            if (country!=null){
                City city = country.getCity();
                if (city != null){
                    return city.getName();
                }
            }
        }

        return "unknown";
    }
复制代码

能够看到上面的检查有多么繁杂,代码中充斥着空检查,可读性糟糕透顶。设计

Optional 类入门

变量存在时, Optional 类只是对类简单封装。变量不存在时,缺失的值会被建模成一个“空” 的 Optional 对象,由方法 Optional.empty() 返回。 那你可能就会疑惑,null 和 Optional.empty() 的区别在哪呢?从语义上,你能够把它们看成一回事儿,可是实际中它们之间的差异很是 大 : 若是你尝试解引用一个 null , 必定会触发NullPointerException , 不过使用 Optional.empty() 就彻底没事儿,它是 Optional 类的一个有效对象,多种场景都能调用,很是有用。3d

应用 Optional 的几种模式

建立 Optional 对象实例

能够建立一个空的 Optional 对象实例指针

@Test(expected = NoSuchElementException.class)
    public void createOptionalObject(){
        Optional<String> country = Optional.empty();
        country.get();
    }
复制代码

毫无疑问,当咱们调用 get() 方法会报 NoSuchElementException 异常code

还可使用 of() 和 ofNullable() 方法建立包含值的 Optioanal 实例,区别在于若是将 null 看成参数传进去 of() 会报空指针异常,因此对象可能存在或者不存在,应该使用 ofNullable()cdn

@Test
    public void createOptionalObject(){
        Optional<String> country = Optional.of("中国");
        Optional<String> city = Optional.ofNullable("上海");
        Optional<String> world = Optional.ofNullable(null);
        //下面会报空指针异常
        Optional<String> province = Optional.of(null);
    }
复制代码

如何获取Optional变量中的值 ?Optional 提供了一个 get() 方法。不过 get方法在遭遇到空的Optional对象时也会抛出异常,因此不按照约定的方式使用它,又会让咱们再度陷入由null引发的代码维护的梦魇。对象

访问 Optional 对象的值

从 Optional 实例中取回实际值对象的方法之一是使用 get() 方法:blog

@Test
    public void getOptionalObject(){
        String country = "China"
        Optional<String> countryName = Optional.ofNullable(country);
        
        Assert.assertEquals("China",countryName.get());
    }
复制代码

固然这个方法会在值为null时抛出异常,要避免异常,首先要进行检查

@Test
    public void getOptionalObject(){
        City city = new City("ShangHai");
        Optional<City> sample = Optional.ofNullable(city);
        Assert.assertTrue(sample.isPresent());

        Assert.assertEquals(city.getName(),sample.get().getName());
    }
复制代码

检查是否有值还有另一个方法 ifPresent(),该方法除了检查还会传入一个 Consumer(消费者) 参数,若是对象不是空的,就会执行传入的 Lambda 表达式

@Test
    public void getOptionalObject(){
        City city = new City("ShangHai");
        Optional<City> sample = Optional.ofNullable(city);
        sample.ifPresent(c -> Assert.assertEquals(city.getName(),sample.get().getName()));
    }
复制代码

若是对象不为空则为执行断言

返回默认值

Optional 提供了 API 用以返回对象值,或者在对象为空的时候返回默认值

@Test
    public void getOptionalObject(){
        City city = null;
        City city1 = new City("ShangHai");
        City sample = Optional.ofNullable(city).orElse(city1);
        Assert.assertEquals(city1.getName(),sample.getName());
    }
复制代码

第二个同类型的 API 是 orElseGet() —— 其行为略有不一样。这个方法会在有值的时候返回值,若是没有值,它会执行做为参数传入的 Supplier(供应者) 函数式接口,并将返回其执行结果:

City sample = Optional.ofNullable(city).orElseGet(() -> city1);
复制代码

返回异常

Optional 还定义了 orElseThrow() API 它会在对象为空时抛出异常

@Test(expected = IllegalArgumentException.class)
    public void throwOptionalException(){
        City city = null;
        City sample = Optional.ofNullable(city).orElseThrow(() -> new IllegalArgumentException());
    }
复制代码

city 为空因此会抛出 IllegalArgumentException

这个方法让咱们有更丰富的语义,能够决定抛出什么样的异常,而不老是抛出 NullPointerException。

使用 Optional 的实战实例

使用map从 Optional 对象中提取和转换值

从对象中提取信息是一种比较常见的模式,为了支持这种模式,Optional提供了一个map方法。它的工做方式以下:

@Test
    public void getCityName(){
        City city = new City("ShangHai");
        Optional<City> sample = Optional.ofNullable(city);
        Optional<String> name = sample.map(City::getName);
    }
复制代码

map 对值应用(调用)做为参数的函数,而后将返回的值包装在 Optional 中,这就使对返回值进行链试调用的操做成为可能,那是否是就能够对以前的代码进行重构呢?

public Optional<String> getCityName(World world){

        Optional<World> real = Optional.ofNullable(world);
        Optional<String> name =
                real.map(World::getCountry)
                    .map(Country::getCity)
                    .map(City::getName);

        return name;
    }
复制代码

可是这段代码没法经过编译,real.map(World::getCountry) 返回的是 Optional 的实例这个没有问题,可是后面继续调用map产生的就是 Optional<Optional>类型的对象。说明你遭遇了嵌套式的 Optional 机构。

optional

两层Optional对象结构

使用 flatMap 连接 Optional 对象

因此,咱们该如何解决这个问题呢?让咱们再回顾一下你刚刚在流上使用过的模式: flatMap 方法。使用流时, flatMap 方法接受一个函数做为参数,这个函数的返回值是另外一个流。 这个方法会应用到流中的每个元素,最终造成一个新的流的流。可是 flagMap 会用流的内容替 换每一个新生成的流。换句话说,由方法生成的各个流会被合并或者扁平化为一个单一的流。这里 你但愿的结果其实也是相似的,可是你想要的是将两层的 optional 合并为一个。

public Optional<String> getCityName(World world){

        Optional<World> real = Optional.ofNullable(world);
        Optional<String> name =
                real.flagMap(World::getCountry)
                    .flagMap(Country::getCity)
                    .map(City::getName);

        return name;
    }
复制代码

使用 filter 剔除特定的值

你常常须要调用某个对象的方法,那么你首先要检查对象是否为NULL

public void filterCity(City city){

    Optional<City> real = Optional.ofNullable(city);
    real.filter(c -> c!=null && "ShangHai"
            .equals(c.getName()))
            .ifPresent(x -> System.out.println("ok"));

}
复制代码

小结

  1. null 引用在历史上被引入到程序设计语言中,目的是为了表示变量值的缺失。
  2. Java 8中引入了一个新的类 java.util.Optional,对存在或缺失的变量值进行 建模。
  3. 你可使用静态工厂方法 Optional.empty、 Optional.of 以及 Optional.ofNullable 建立 Optional 对象。
  4. Optional类支持多种方法,好比 map、 flatMap、 filter,它们在概念上与 Stream 类中对应的方法十分类似。
  5. 使用 Optional 会迫使你更积极地解引用 Optional 对象,以应对变量值缺失的问题,最终,你能更有效地防止代码中出现不期而至的空指针异常。
  6. 使用 Optional 能帮助你设计更好的 API,用户只须要阅读方法签名,就能了解该方法是否接受一个 Optional类型的值。
相关文章
相关标签/搜索