Hi,大家好,我是抢老婆酸奶的小肥仔。

在咱们日常开发中NullPointerException是永久绕不过去的坑,代码写的欠好,天天都会被它摧残的死去活来,今日咱们来介绍下Optional,这个能让咱们缓解NullPointerException惊骇的工具。

在没有Optional之前,咱们为了防止NullPointerException许多时候都是运用if-else-if来进行条件判别,然后根据条件来获取对应数据。例如:获取赵一所在的职位称号。

List<Expert> experts = Arrays.asList(
                new Expert("jiashn","男",20,new Position("总司理",1)),
                new Expert("张三","女",21,new Position("财务总监",2)),
                new Expert("王五","女",26,new Position("管帐",3)),
                new Expert("李四","男",24,new Position("开发部司理",2))
        );
//获取赵一所在的职位称号
if(CollectionUtils.isNotEmpty(experts)){
    for (Expert expert : experts) {
        if (Objects.equals("赵一",expert.getName())){
            Position position = expert.getPosition();
            if (Objects.nonNull(position)){
                System.out.println(position.getName());
            }
        }
    }
}

运用optional就会高雅许多,也会尽量的防止NPE。如:

String name = Optional.of(users.stream().filter(user -> Objects.equals("赵一", user.getName())).findFirst())
                .get()
                .map(SystemUser::getPosition)
                .map(Position::getName)
                .orElse("");
        System.out.println("Optional获取职位称号:" + name);

上述代码比照中,咱们能够明晰感觉到运用Optional代码比较高雅,也没有很多代码判空,即使数据不存在也没报NPE,防止了NPE.

Optional是JDK1.8后引入的一个新特性,通常被认为是用于缓解java中NPE问题。Optional作为容器,能够保存类型T的值,也能够直接保存null。

接下来咱们介绍下Optional的一些办法。

Optional办法

准备工作,创立一个有数据的系统用户,一个为null的数据。

/**
 * @author: jiangjs
 * @description: 系统用户
 * @date: 2023/5/8 11:40
 **/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SystemUser {
    private String name;
    private String gender;
    private Integer age;
    private Position position;
    public Optional<Position> optionalPosition(){
        return Optional.ofNullable(position);
    }
}
/**
 * @author: jiangjs
 * @description: 职位
 * @date: 2023/5/8 11:20
 **/
@Data
@AllArgsConstructor
public class Position {
    /**
     * 职位称号
     */
    private String name;
    /**
     * 职位等级
     */
    private Integer level;
}
 SystemUser systemUser = new SystemUser("王五","女",26,new Position("管帐",3));
 SystemUser user = null;

empty

static<T> Optional<T> empty() :静态办法,回来空的Optional。

源码:

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

实现:

//empty
Object o = Optional.empty();
System.out.println("empty:" + o);

输出:

empty:Optional.empty

of、ofNullable

static <T> Optional<T> of(T value) :创立一个非空的Optional,当T为null时,则抛出NPE。

static <T> Optional<T> ofNullable(T value) :假如T不为空,则创立指定值的Optional,不然回来空的Optional。

//of ofNullable
Optional<SystemUser> systemUserOptional = Optional.of(systemUser);
System.out.println("systemUserOptional:" + systemUserOptional);
Optional<SystemUser> userOptional = Optional.of(user);
System.out.println("userOptional:" + userOptional);
Optional<SystemUser> optionalSystemUser = Optional.ofNullable(systemUser);
System.out.println("optionalSystemUser:" + optionalSystemUser);
Optional<SystemUser> optionalUser = Optional.ofNullable(user);
System.out.println("optionalUser:" + optionalUser);

输出:

systemUserOptional:Optional[SystemUser(name=王五, gender=女, age=26, position=Position(name=管帐, level=3))]

java.lang.NullPointerException

optionalSystemUser:Optional[SystemUser(name=王五, gender=女, age=26, position=Position(name=管帐, level=3))]

optionalUser:Optional.empty

Optional.of(user):报了空指针异常,这是由于of在创立Optional时,会对T进行判空处理。源码:

public static <T> Optional<T> of(T value) {
    return new Optional<>(value);
}
private Optional(T value) {
    this.value = Objects.requireNonNull(value);
}

Optional.ofNullable(user):运用了三目运算符,假如T为空,则调用empty(),回来空的Optional。
源码:

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

isPresent()

isPresent() :判别是否包括指定值,包括回来true,反之回来false。

//isPresent
boolean present = Optional.of(systemUser).isPresent();
System.out.println("systemUser是否存在:" + present);
boolean judge = Optional.ofNullable(user).isPresent();
System.out.println("user是否存在:" + judge);

输出:

systemUser是否存在:true

user是否存在:false

isPresent():直接获取Optional中的T来判空。源码:

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

注:在写代码时,不要即用了if,又用isPresent来进行判别,如:if (Optional.ofNullable(user).isPresent()){事务代码},这样消除不了繁琐的if判别,也创立了Optional目标。

orElse、orElseGet

T orElseGet(Supplier<? extends T> other) :假如调用目标包括值,则回来该值,不然回来other。

T orElse(T other) :假如调用目标包括值,则回来该值,不然回来other。

//orElseGet,orElse
SystemUser orElseGetUser = Optional.ofNullable(user).orElseGet(UseOptional::getSystemUser);
System.out.println("orElseGetUser:" + orElseGetUser);
SystemUser orElseUser = Optional.ofNullable(user).orElse(getSystemUser());
System.out.println("orElseUser:" + orElseUser);
System.out.println("-------------------------------------------------------------");
SystemUser orElseGetSystemUser = Optional.of(systemUser).orElseGet(UseOptional::getSystemUser);
System.out.println("orElseGetSystemUser:" + orElseGetSystemUser);
SystemUser orElseSystemUser = Optional.of(systemUser).orElse(getSystemUser());
System.out.println("orElseSystemUser:" + orElseSystemUser);

输出:

默认数据:SystemUser(name=李四, gender=男, age=24, position=Position(name=开发部司理, level=2))

orElseGetUser:SystemUser(name=李四, gender=男, age=24, position=Position(name=开发部司理, level=2))

默认数据:SystemUser(name=李四, gender=男, age=24, position=Position(name=开发部司理, level=2))

orElseUser:SystemUser(name=李四, gender=男, age=24, position=Position(name=开发部司理, level=2))


orElseGetSystemUser:SystemUser(name=王五, gender=女, age=26, position=Position(name=管帐, level=3))

默认数据:SystemUser(name=李四, gender=男, age=24, position=Position(name=开发部司理, level=2))

orElseSystemUser:SystemUser(name=王五, gender=女, age=26, position=Position(name=管帐, level=3))

当调用目标不包括值时,尽管两者都是回来other,可是从界说中咱们能够看到orElseGet是运用了Supplier函数式编程,而orElse直接运用T。上面例子也说明晰orElse会提早创立T而orElseGet会在判别调用目标没有时才会调用Supplier来创立other。

orElse与orElseGet差异:

orElse:不管调用目标是否存在,都会提早创立T。

orElseGet:只有当调用目标不存在时,才会运用Supplier创立回来值。

get

get():即获取Optional的目标数据,假如目标数据不存在则抛出异常:NoSuchElementException

//get
SystemUser getSystemUser = Optional.ofNullable(systemUser).get();
System.out.println("getSystemUser:" + getSystemUser);
SystemUser getUser = Optional.ofNullable(user).get();
System.out.println("getUser:" + getUser);

输出:

getSystemUser:SystemUser(name=王五, gender=女, age=26, position=Position(name=管帐, >level=3))

Exception in thread “main” java.util.NoSuchElementException: No value present

at java.util.Optional.get(Optional.java:135)

at com.jiashn.springbootproject.useUtil.UseOptional.main(UseOptional.java:86)

map、flatMap

<U> Optional<U> map(Function<? super T, ? extends U> mapper) :假如有值,则进行数据处理,假如没有值,则回来Optional.empty()。

<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) :与map相似,要求回来值必须是Optional。

//map
String sysUserName = Optional.of(systemUser).map(SystemUser::getName).get();
System.out.println("sysUserName:" + sysUserName);
Optional<String> userName = Optional.ofNullable(user).map(SystemUser::getName);
System.out.println("userName:" + userName);
//flatMap
String name = Optional.of(systemUser)
        .flatMap(SystemUser::optionalPosition)
        .map(Position::getName).get();
System.out.println("name:" + name);

输出:

sysUserName:王五

userName:Optional.empty

name:管帐

上述便是Optional提供一些办法,咱们在实际开发中应灵活运用,通常Optional会跟Stream进行联合运用。例如:获取上述调集性别为女的,然后获取过滤后调集的第一个元素,数据不存在时创立新的目标。

Expert expert = Optional.of(experts.stream().filter(ex -> Objects.equals("女",ex.getGender())).findFirst())
                .get()
                .orElseGet(Expert::new);

这样子在后面运用expert就能防止出现NPE.

jdk引入了Optional能让咱们快捷的获取比较复杂的数据结构中的数据,同时也能有用防止NPE,当然Optional办法单独运用可能作用不大,可是组合运用时会让咱们事半功倍。