Optional类
Optional<T>类是一个容器类,代表一个值存在和不存在,原来用不惯null表示一个值的不存在,现在用Optional可以更好的表达这个概念;是Java1.8之后解决null值判断问题。
常用方法列表
| method | desc | 
|---|---|
| Optional.of(T t) | 创建一个非空值的Optional实例 | 
| Optional.empty() | 创建一个空的Optional实例 | 
| Optional.ofNullable(T t) | 若t不为null,创建Optional实例,否则创建空实例 | 
| isPresent() | 判断是否包含值 | 
| ifPresent(Consumer c) | 如果存在值,则使用该值调用指定的消费者,否则不执行任何操作 | 
| get() | 如果 Optional中有值,返回值,否则抛出NoSuchElementException | 
| orElse(T t) | 如果对象包含值,返回该值,否则返回t | 
| orElseGet(Supplier s) | 如果对象包含值,返回该值,否则返回s中获取的值 | 
| orElseThrow(Supplier s) | 如果容器为空,返回自定义异常信息 | 
| filter(Predicate p) | 断言型接口返回true,则返回Optional描述的值;否则返回Optional.empty() | 
| map(Function f) | 如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty() | 
| flatmap(Function mapper) | 与map类似,返回值要求必须是Optional | 
引入
看一下下面的代码,为了避免空指针异常,我们需要多层的if判断,不利于代码的易读性同时也不利于书写。
    if (people != null) {
        Country country = people.getCountry();
        if (country != null) {
            City city = country.getCity();
            if (city != null) {
                Street street = city.getStreet();
                if (street != null) {
                    String streetName = street.getStreetName();
                }
            }
        }
    }
创建实例方式
- 
Optional.of(T t) public void test02() { Optional<People> p = Optional.of(new People()); People people = p.get(); }若 Optional.of(null),无法创建实例,导致NullPointerException
- 
Optional.empty() public void test03() { Optional<People> p = Optional.empty(); People people = p.get(); }创建Optional的空实例,若使用 p.get()获取对象,会导致java.util.NoSuchElementException: No value present
- 
Optional.ofNullable(T t) public void test04() { Optional<People> p = Optional.ofNullable(new People()); People people = p.get(); System.out.println(people); }使用 Optional.ofNullable构建Optional实例时,参数可以为其他实例,也可以为null,如果为null时,默认返回的是Optional.empty。// 源码 public static <T> Optional<T> ofNullable(T value) { return value == null ? empty() : of(value); }
获取Optional的值
    public void test04() {
        Optional<People> p = Optional.ofNullable(new People());
        People people = p.get();
        System.out.println(people);
    }
Optional容器类通过get()方法获取容器中的对象,但若是Optional是个空实例,就会发生NoSuchElementException异常,所以Optional容器类提供了isPresent()方法来判断Optional是否是空实例。
    public void test04() {
        Optional<People> p = Optional.ofNullable(new People());
        if (p.isPresent()) {
            People people = p.get();
            System.out.println(people);
        }
    }
    public void test05() {
        String name = "zhangsan";
        Optional<String> optional = Optional.ofNullable("zhangsan");
        Assert.assertTrue(optional.isPresent());
        Assert.assertEquals(name, optional.get());
    }
检查Optional容器类是否有值另外一个方法ifPresent(Consumer c); 如果有值则调用消费者代码块,否则不执行任何操作。若有值而Consumer为空,则会导致NullPointerException
    public void test06() {
        Optional<String> optional = Optional.ofNullable("zhangsan");
        optional.ifPresent(System.out::println);
    }
兜底函数
兜底函数可以理解为默认值,当Optional容器中没有值,则会返回兜底函数的值,避免获取不到值的情况
orElse(T t)
如果对象包含值,返回该值,否则返回t (注意:返回的是具体的值,而不是Optional容器类)
    public void test07() {
        String name = null;
        String username = Optional.ofNullable(name).orElse("zhangsan");
        Assert.assertEquals("zhangsan", username);
    }
orElseGet(Supplier s)
如果对象包含值,返回该值,否则执行Supplier代码块
public void test08() {
    String name = null;
    People people = new People("zhangsan", 18, null);
    String username = Optional.ofNullable(name).orElseGet(people::getName);
    Assert.assertEquals("zhangsan", username);
}
orElse和orElseGet对比
若传入的值为null时
    @Test
    public void test09() {
        System.out.println("orElse");
        String name1 = (String) Optional.ofNullable(null).orElse(getName());
        System.out.println("orElseGet");
        String name2 = (String) Optional.ofNullable(null).orElseGet(() -> getName());
    }
    public String getName() {
        System.out.println("getName");
        return "zhangsan";
    }
结果输出
orElse
getName
orElseGet
getName
可以看出,两者并没有差异,都会调用getName()方法
若传入非null值
    public void test10() {
        System.out.println("orElse");
        String name1 = (String) Optional.ofNullable("lisi").orElse(getName());
        System.out.println("orElseGet");
        String name2 = (String) Optional.ofNullable("lisi").orElseGet(() -> getName());
    }
结果输出
orElse
getName
orElseGet
若传入非null值时,orElse()还是会调用getName(),而orElseGet()则不会执行。
在密集型调用,或者需求量较大时,两者性能上的差异显而易见。
orElseThrow(Supplier s)
如果Optional容器为空,可以返回自定义的异常信息
    public void test11() {
        Object o = Optional.ofNullable(null).orElseThrow(IllegalArgumentException::new);
    }
值过滤
filter(Predicate p)
通过filter(Predicate p)方法过滤出来符合要求的值,其中当断言型接口p返回true,则返回Optional描述的值;否则返回空的Optional
    public void test12() {
        People p = new People("zhangsan", 11, null);
        Optional<People> people = Optional.ofNullable(p)
                .filter(x -> x.getAge() == 11);
        Assert.assertTrue(people.isPresent());
    }
值转换
map()
如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty();
    public void test13() {
        People p = new People("zhangsan", 11, null);
        Optional<String> optionalS = Optional.ofNullable(p).map(x -> x.getName());
        Assert.assertTrue(optionalS.isPresent());
        
        Assert.assertEquals("zhangsan", optionalS.get());
    }
flatmap(Function mapper)
与map类似,返回值要求必须是Optional
    public void test14() {
        People p = new People("zhangsan", 11, null);
        Optional<String> optionalS = Optional.ofNullable(p).flatMap(x -> Optional.ofNullable(x.getName()));
        Assert.assertTrue(optionalS.isPresent());
        Assert.assertEquals("zhangsan", optionalS.get());
    }
总结
链式编程
filter,map,fliterMap返回都是Optional描述的值,所以就可以使用链式编程
    public void test15() {
        People p = null;
        String str = Optional.ofNullable(p)
                .filter(x -> x.getName() != null)
                .flatMap(x -> Optional.ofNullable(x.getName()))
                .orElse("lisi");
        Assert.assertEquals(str, "lisi");
    }
引入的优化
// 每个属性值都是用Optional封装
private Optional<Country> country;
    public void test20() {
        People p = null;
        String s = Optional.ofNullable(p)
                .flatMap(x -> x.getCountry())
                .flatMap(x -> x.getCity())
                .flatMap(x -> x.getStreet())
                .map(x -> x.getStreetName())
                .orElse("北京东路");
        Assert.assertEquals("北京东路", s);
    }