Optional用好了,缓解了多年NullPointerException恐惧。

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

在我们日常开发中NullPointerException是永远绕不过去的坑,代码写的不好,天天都会被它折磨的死去活来,今天我们来介绍下Optional,这个能让我们缓解NullPointerException恐惧的工具。

在没有Optional之前,我们为了避免NullPointerException很多时候都是使用if-else-if来进行条件判断,然后根据条件来获取对应数据。例如:获取赵一所在的职位名称。

java 复制代码
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。如:

java 复制代码
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的数据。

java 复制代码
/**
 * @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;

}
java 复制代码
 SystemUser systemUser = new SystemUser("王五","女",26,new Position("会计",3));
 SystemUser user = null;

empty

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

源码:

java 复制代码
private static final Optional<?> EMPTY = new Optional<>();

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

实现:

java 复制代码
//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。

java 复制代码
//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进行判空处理。源码:

java 复制代码
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。 源码:

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

isPresent()

isPresent() :判断是否包含指定值,包含返回true,反之返回false。

java 复制代码
//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来判空。源码:

java 复制代码
 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。

java 复制代码
//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

java 复制代码
//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。

java 复制代码
//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进行联合使用。例如:获取上述集合性别为女的,然后获取过滤后集合的第一个元素,数据不存在时创建新的对象。

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

这样子在后面使用expert就能避免出现NPE.

jdk引入了Optional能让我们快捷的获取比较复杂的数据结构中的数据,同时也能有效避免NPE,当然Optional方法单独使用可能效果不大,但是组合使用时会让我们事半功倍。

相关推荐
向前看-10 分钟前
验证码机制
前端·后端
xlsw_12 分钟前
java全栈day20--Web后端实战(Mybatis基础2)
java·开发语言·mybatis
神仙别闹1 小时前
基于java的改良版超级玛丽小游戏
java
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭1 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
暮湫2 小时前
泛型(2)
java
超爱吃士力架2 小时前
邀请逻辑
java·linux·后端
南宫生2 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
转码的小石2 小时前
12/21java基础
java
李小白662 小时前
Spring MVC(上)
java·spring·mvc
GoodStudyAndDayDayUp2 小时前
IDEA能够从mapper跳转到xml的插件
xml·java·intellij-idea