告别空指针,Optional的最佳使用姿势!

👉 这是一个或许对你有用的社群

🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入「芋道快速开发平台」知识星球。下面是星球提供的部分资料: 

4d7274f8c0a08894b472dc14ad2a3ab3.gif

👉这是一个或许对你有用的开源项目

国产 Star 破 10w+ 的开源项目,前端包括管理后台 + 微信小程序,后端支持单体和微服务架构。

功能涵盖 RBAC 权限、SaaS 多租户、数据权限、商城、支付、工作流、大屏报表、微信公众号、ERP、CRMAI 大模型等等功能:

  • Boot 多模块架构:https://gitee.com/zhijiantianya/ruoyi-vue-pro

  • Cloud 微服务架构:https://gitee.com/zhijiantianya/yudao-cloud

  • 视频教程:https://doc.iocoder.cn

【国内首批】支持 JDK 17/21 + SpringBoot 3.3、JDK 8/11 + Spring Boot 2.7 双版本 

来源:潘志的研发笔记

99ba6778e1a6cd81c9d0a59fd56d181d.jpeg


一、背景介绍

NullPointerException,中文名:空指针异常 ,也简称 NPE,是软件系统中最常见的错误异常之一。

很久以前 Google Guava 项目引入了Optional作为解决空指针异常的一种方式,不赞成写过多的代码来显式检查null,以期望程序员写出整洁同时可读性更高的代码。

受 Google Guava 的影响,Optional 现在也成为了Java 8 及以上库代码的一部分。

在介绍Optional技术之前,我们不禁会发出一个疑问:为什么谷歌不赞成写过多的代码来显式检查null

下面是某个常见的参数判空代码,样例如下。

// 判断行政区是否为空
if(country != null){
    // 判断行政区的上一级,行政城市是否为空
    if(country.getCity() != null){
        // 判断行政城市的上一级,行政省是否为空
        if(country.getCity().getProvince() != null){
            // 获取对应的行政省相关的数据
            return country.getCity().getProvince().getName();
        }
    }
}

这还是最普通的三层判断,假如有很大一段业务逻辑处理的时候,你会发现代码不光看起来很臃肿,并且难以阅读,可读性很差!

如果调整为使用Optional来编写的话,可以转换成如下写法:

// 获取当前行政区最顶级的省信息名称
String result  = Optional.ofNullable(country)
                .map(Country::getCity)
                .map(City::getProvince)
                .map(Province::getName)
                .orElse("error");

采用Optional来编程之后,整个代码的可读性和整洁度,是不是要干净很多!

这也是为什么推荐大家使用Optional的原因啦!

当然废话也不多说,代码直接撸起来!

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro

  • 视频教程:https://doc.iocoder.cn/video/

二、案例实践

在 JDK8 中,Optional 共有 12 个核心方法,下面我们一起来看看他们的用法!

2.1、empty()

empty 方法返回一个不包含值的 Optional 实例,单独使用没什么意义,主要和其他方法搭配使用。

Optional optional = Optional.empty();
System.out.println(optional);
-- 输出结果
Optional.empty
2.2、of()

of 方法会返回一个 Optional 实例,如果传入的值非空,会返回包含指定值的对象;如果传入空,会立刻抛出空指针异常。

// 非空情况下,会正常返回
Optional optional = Optional.of("hello world");
System.out.println(optional);
-- 输出结果
Optional[hello world]
// 为空情况下,会抛空指针异常
Optional optional = Optional.of(null);
System.out.println(optional);
-- 输出结果
Exception in thread "main" java.lang.NullPointerException
 at java.util.Objects.requireNonNull(Objects.java:203)
 at java.util.Optional.<init>(Optional.java:96)
 at java.util.Optional.of(Optional.java:108)
2.3、ofNullable()

ofNullable 方法会返回一个 Optional 实例,如果传入的值非空,会返回包含指定值的对象;如果传入空,会返回不包含任何值的 empty 对象,也就是最开始介绍的Optional.empty()对象。

// 非空情况下,会正常返回
Optional optional = Optional.ofNullable("hello world");
System.out.println(optional);
-- 输出结果
Optional[hello world]
// 为空情况下,会返回 empty 对象
Optional optional = Optional.ofNullable(null);
System.out.println(optional);
-- 输出结果
Optional.empty
2.4、isPresent()

isPresent 方法用来判断实例是否包含值,如果包含非空值,返回 true,否则返回 false。

// 非空值,返回true
boolean rs1 =  Optional.ofNullable("hello").isPresent();
System.out.println(rs1);

// 空值,返回false
boolean rs2 =  Optional.ofNullable(null).isPresent();
System.out.println(rs2);
-- 输出结果
true
false
2.5、get()

get 方法,如果实例包含非空值,则返回当前值;否则抛出 NoSushElementException 异常。

// 非空值,返回当前值
Object rs =  Optional.ofNullable("hello world").get();
System.out.println(rs);
-- 输出结果
hello world
// 空值,会抛出 NoSushElementException 异常
Object rs =  Optional.ofNullable(null).get();
System.out.println(rs);
-- 输出结果
Exception in thread "main" java.util.NoSuchElementException: No value present
 at java.util.Optional.get(Optional.java:135)
2.6、ifPresent()

ifPresent 方法作用是当实例包含非空值时,执行传入的 Consumer,比如调用一些其他方法;如果包含的值为空,不执行任何操作。

Optional.ofNullable("hello world")
                .ifPresent( x -> {
                    System.out.println(x);
                });
-- 输出结果
hello world
2.7、filter()

filter 方法用于过滤不符合条件的值,接收一个Predicate参数,如果符合条件,会返回当前的Optional实例,否则返回 empty 实例。

Optional.ofNullable("hello world")
                .filter(x -> x.contains("hello"))
                .ifPresent(x -> {
                    System.out.println(x);
                });
-- 输出结果
hello world
2.8、map()

map 方法是链式调用避免空指针的核心方法,当实例包含值时,对值执行传入的Function函数接口方法,并返回一个代表结果值新的Optional实例,也就是将返回的结果再次包装成Optional对象。

Optional.ofNullable("hello+world")
                .map(t -> {
                    if(t.contains("+")){
                        return t.replace("+", " ");
                    }
                    return t;
                }).ifPresent(t -> {
                    System.out.println(t);
                });
-- 输出结果
hello world
2.9、flatMap()

flatMap 方法与 map 方法类似,唯一不同的地方在于:需要手动将返回的值,包装成Optional实例,并且参数值不允许为空

Optional.ofNullable("hello+world")
                .flatMap(t -> {
                    if(t.contains("+")){
                        t =  t.replace("+", " ");
                    }
                    // 不同之处
                    return Optional.of(t);
                }).ifPresent(t -> {
                    System.out.println(t);
                });
-- 输出结果
hello world
2.10、orElse()

orElse 方法作用是如果实例包含非空值,那么返回当前值;否则返回指定的默认值。

Object rs =  Optional.ofNullable(null).orElse("null");
System.out.println(rs);
-- 输出结果
null
2.11、orElseGet()

orElseGet 方法作用是如果实例包含非空值,返回这个值;否则,它会执行作为参数传入的Supplier函数式接口方法,并返回其执行结果。

Object result = Optional.ofNullable(null)
                .orElseGet(() -> {
                    return "error";
                });
System.out.println(result);
-- 输出结果
error
2.12、orElseThrow()

orElseThrow 方法作用是如果实例包含非空值,返回这个值;否则,它会执行作为参数传入的异常类。

Optional.ofNullable(null)
                .orElseThrow(() -> new RuntimeException("参数为空"));
-- 输出结果
Exception in thread "main" java.lang.RuntimeException: 参数为空
 at com.x.x.x.x.OptionalTest.lambda$main$10(OptionalTest3.java:144)
 at java.util.Optional.orElseThrow(Optional.java:290)

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/yudao-cloud

  • 视频教程:https://doc.iocoder.cn/video/

三、小结

以上就是 JDK8 新增的Optional类的常用方法总结,其中ofNullablemaporElse方法搭配使用的最多。

另外orElseorElseGetorElseThrow区别如下:

  • orElse:如果实例包含空值,返回传入指定的值

  • orElseGet:如果实例包含空值,返回传入的方法中返回值

  • orElseThrow:如果实例包含空值,返回指定的异常类型

在实际使用的时候,还得结合具体的场景进行合理选择,有时候并不是全部采用Optional来解决NPE异常代码才更加优雅,比如当前对象比较简单,就是一个简单判断,通过obj != null足以解决问题。

因此在保证业务功能的正确和稳定性的基础之上,适当的选择相关的工具来优化代码的整洁度和可读性,更能发挥出锦上添花的效果!


欢迎加入我的知识星球,全面提升技术能力。

👉 加入方式,长按”或“扫描”下方二维码噢

b28adf13e1efecf97c5a891ab904f719.png

星球的内容包括:项目实战、面试招聘、源码解析、学习路线。

5a11c03f4c4beb5c70e5cd8e5113f41b.png

3c7acc7e2af535d489aa268f5856812d.pnga363abf0075a0da77c7b1bbf698d7aa7.png0e05f0e04f2fffc2ee52d6016678ed7a.png785b149d9708684dc9fcaf4300011f4d.png

文章有帮助的话,在看,转发吧。
谢谢支持哟 (*^__^*)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值