Optional 使用背景

Java 8 引入的 Optional 主要是用来解决 NullPointerException 的问题的。通过链式方法调用和Lambda表达式可以优雅的解决层层判空、繁琐的if语句的问题。但是 Optional 也降低了代码的可读性,虽然老式的层层if判空繁琐,但是可读性非常高,一眼就看的懂什么意思。Optional 处理空的时候,必须的对其了解,不然可能就不知道什么意思了。下面就对 Optional 的各个方法的使用和其源码进行分析

分析各个方法

创建Optional对象
Optional 是对我们使用的对象的一个包装,我们是用的时候是直接对 Optional 对象,从而间接的操作原对象。Optional 对象的两个构造函数都是 private 的,所以我们只能通过,of 和 ofNullable 两个静态方法去构造 Optional 对象。从名称上即可得知 ofNullable 是可以构造 原对象为空的Optional。下面看两者的源码:

public static <T> Optional<T> of(T value) {   
    return new Optional<>(value);
}
private Optional(T value) {
    this.value = Objects.requireNonNull(value);
}
public static <T> Optional<T> ofNullable(T value) {   
    return value == null ? empty() : of(value);
}
//例子
Optional<User> o = Optional.ofNullable(u);

of 方法是直接调用带有参数的构造, ofNullable 是通过原对象是否为空来选择创建一个空的Optional对象,或者通过原对象创建一个Optional对象。of 构建时会有非空判断,依然会抛出空指针异常,所以一般选择 ofNullable 来构建Optional对象。

ifPresent() / ifPresent(Consumer<? super T> consumer)
ifPresent是用来判断原对象是否为空的,这两个方法的区别就是一个是没有参数直接返回是否为空的布尔值,另外一个是带参数的的,通过代码可以看出,非空是会执行接口方法的。

 public void ifPresent(Consumer<? super T> consumer) {
    if (value != null)
        consumer.accept(value);
}
public boolean isPresent() {
    return value != null;
}
//例子
System.out.println(Optional.ifPresent(u));//判断User是否为空
Optional.ifPresent(u->System.out.println(u.getUserName()));//如果User不为空则输出userName

get
get是用来直接获取包装的Optional原对象的值的,但是当值为空时会报异常,而且一般我们也不会直接是用get来获取值。

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

map /flatMap
这两个方法是对传入的方法返回值的继续包装,形成一个链式的方法,以便连续判空是用,他们主要区别是参数不同,flatMap需要传入的参数已经是被Optional 包装过的,而map会直接对传入的参数进行包装,看他们的会返回处理方式也可以得知。

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

//例子
Optional<String> nameOptional = Optional.ofNullable(u).map(u->u.getName);

一般使用map + orElse/orElseGet/orElseThrow 来完成大部分的判空链式操作

orElse/orElseGet/orElseThrow
三个方法都是在是在对象非空时返回原对象,为空时,做一些操作:orElse 是将参数值返回,orElseGet是将“传入函数”返回值返回,orElseThrow则是抛出一个异常

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();   
    }
}

//例子
User u = Optional.ofNullable(u).orElse(new User());
User u = Optional.ofNullable(u).orElseGet(()->new User());
User u = Optional.ofNullable(u).orElseThrow(()->new NullPointerException());

filter
filter类似于一个过滤条件将不满足条件的对象过滤掉,传入一个参数为原对象,返回值为 boolean 的函数,如果 predicate.test 不成立就返回一个empty的空对象。

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

Optional<User> result = Optional.ofNullable(user).filter(u->!"zhangsan".equals(u.getUserName()));//得到姓名不为zhangsan的包装对象,否则返回空的包装对象

实际中的使用例子
假设有一个 AssetsInfo 对象 他有一个 ComponentInfo 的List对象属性,ComponentInfo里面有一个category 字段,现在想获取一个List 对象 第一个元素,的第一个 ComponentInfo 的category 的值,如果中间环节有空指针或者 list.size为0,最后返回的category为“”空字符串

//伪代码大概时这样的
String category = assetsInfoDoList.get(0).getComponentInfoDoList().get(0).getCategory();

如果按照之前的写法大致代码如下:

String category = "";
if(assetsInfoDoList !=null && assetsInfoDoList.size() !=0){   
    AssetsInfoDO assetsInfoDO = assetsInfoDoList.get(0);   
    if(assetsInfoDO.getComponentInfoDoList() != null && assetsInfoDO.getComponentInfoDoList().size() !=0){       
        String tmp  = assetsInfoDO.getComponentInfoDoList().get(0).getCategory();       
        category = tmp;   
    }
}

现在如果使用Optional

String s = Optional.ofNullable(assetsInfoDoList) //得到  assetsInfoDoList的Optional<List<AssetsInfo>>对象
                         .filter(u -> !u.isEmpty()) //过滤list 得到list不为空的对象
                         .map(u -> u.get(0))  //获取元素并自动包装为Optional<AssetsInfo>对象
                         .map(u -> u.getComponentInfoDoList())  //获取素并自动包装为Optional<List<ComponentInfo>>对象      
                         .filter(u -> !u.isEmpty()) //过滤list 得到list不为空的对象      
                         .map(u -> u.get(0))   //获取元素并自动包装为Optional<ComponentInfo>对象     
                         .map(u -> u.getCategory())   //获取元素并自动包装为Optional<String>对象        
                         .orElse(""); //如果ifPresent返回得到的值,否则返回“”空字符串

阅读原文