正确使用 Optional 优雅的解决 null 空指针 NPE 异常
要说起 Optional 的诞生,需要先了解一下 NPE,很多面向对象编程语言中都会有 null 值,也就是空指针,在程序栈上指向了一个不存在的堆地址,当你使用这个指向不存在的堆地址对象时就会抛出一个 NullPointerException 异常,如果你没有很好的遇见到空指针的情况,那么你的程序将会崩溃。
为了解决头疼的 NPE 异常,终于有人看不下去出手了,发明了 Optional 来解决 NPE 的问题。
不要滥用 Optional
在开始学习 Optional 之前,我们需要先学习他解决 NPE 问题的思想,不要滥用 Optional,因为如果滥用 Optional 会发生非常混乱的局面,反而让你的代码更加复杂和难以控制,接下来我们先了解一下他解决 NPE 的思想。
首先明确的是 Optional 是一种容器来存放东西,它只是一种解决空指针的思想,你使用这种思想就可以优雅的解决 NPE 问题,如果你没明白这种思想,即使使用 Optional 也可能会造成 NPE 异常。
禁止滥用 Optional
学会新东西以后觉得好牛逼,希望应用到所有地方,恨不得将所有类都重构一遍,于是可能写出这样的代码:
String userName = Optional.ofNullable(name).orElse("")
这样写虽然没有错,正确使用了 Optional ,但是意义在哪呢?这降低了代码的可读性,并且还创造了一个 Optional 对象,浪费了系统性能,三元表达式完全可以搞定。
禁止使用 Optional 作为 Bean 类的成员
Optional 没有实现Serializable接口(不可序列化),所以可能引起系统故障,Optional 并不是让你作为类成员使用的,正确的使用方法在下面讲。
禁止 Optional 作为 Bean 类的 Setter 方法入参
除了 Optional 是不可序列化的,降低了可读性。Setter 是给成员赋值的,你作为赋值的人你不知道值是不是空吗?所以使用 Optional 作为赋值的入参是没有意义的。
禁止在集合中使用Optional
不要在List, Set, Map 等集合中使用 Optional,因为同样是没有意义的,你这么做想要解决什么?
同样,不要在 Optional 中包装入容器类型,容器一般都有自己的空值逻辑设计,不要画蛇添足。
禁止给Optional赋值null
禁止给 Optional 赋值 null,你应该使用 Optional.empty() 来赋值和表达空。
慎用get()方法
如果不检查 Optional 是否为空值就直接调用 get() 方法,就让 Optional 失去了意义。如果你不确定是否有值,那就永远不要调用 get() 方法。
正确使用Optional
在上面我们先了解了不要滥用 Optional,现在我们了解一些正确使用 Optional 的场景。
当你无法确定拿到的值是否为 null 时,就可以使用 Optional 进行包装,然后处理。
在 Bean 类的 Getter 中使用
在上面举例中禁止在 Bean 类的 Setter 中使用,理由是赋值动作是你主动调用的,你自己应当知道值是否为 null,那么根据这条理由,Bean 类的 Getter 就可以使用 Optional 返回包装值,因为获取值的时候,你无法确定获取到的值是否为 null,就需要 Optional 包装来处理。
优雅的处理复杂对象的NPE问题
在上面案例中,我们举例禁止在简单逻辑中滥用 Optional,例如:
String userName = Optional.ofNullable(name).orElse("")
正确的使用方式是在复杂对象取值时,我们就需要 Optional 来优雅的处理 null 值,举个例子,假设我们有个 User 对象,它有一个BaseInfo 成员,BaseInfo 又拥有 Email 属性,我们不确定 User 是不是 null,也不确定 BaseInfo 是否为 null,想要获取它的 Email,此处将用到多个知识点:
在Bean类 Getter 中使用 Optional,这样可以构成链式调用编程
使用了“
::
”关键字,你也可以使用 lambda 表达式来调用使用了 Optional.flatMap() 方法来转换类型
我们可以这样写:
OptionalExample,可在 https://example.renfei.net/java/OptionalExample/ 获取代码
package net.renfei;
import java.util.Optional;
public class OptionalExample {
public static class User {
private BaseInfo baseInfo;
public void setBaseInfo(BaseInfo baseInfo) {
this.baseInfo = baseInfo;
}
public Optional<BaseInfo> getBaseInfo() {
return Optional.ofNullable(this.baseInfo);
}
public static class BaseInfo {
private String email;
public void setEmail(String email) {
this.email = email;
}
public Optional<String> getEmail() {
return Optional.ofNullable(this.email);
}
}
}
public static void main(String[] args) {
// 验证如果 user 为 null 的情况
printEmail(null);
// 验证如果 User.BaseInfo 为 null 的情况
User user = new User();
user.setBaseInfo(null);
printEmail(user);
// 验证如果 User.BaseInfo.Email 为 null 的情况
User.BaseInfo baseInfo = new User.BaseInfo();
baseInfo.setEmail(null);
user.setBaseInfo(baseInfo);
printEmail(user);
// 验证正常情况
baseInfo.setEmail("i@renfei.net");
user.setBaseInfo(baseInfo);
printEmail(user);
}
public static void printEmail(User user) {
String defaultEmail = "Unknown Email";
// user 是外部传入的,我们也不知道 User 是否为 null
Optional<User> optionalUser = Optional.ofNullable(user);
// 我们需要打印 User 的 Email,如果为 null,打印默认值
System.out.println(optionalUser
.flatMap(User::getBaseInfo)
.flatMap(User.BaseInfo::getEmail)
.orElse(defaultEmail));
}
}
经过上面的代码验证,User 任意成员或属性为 null 时都不会引起 NPE 异常,都正常的输出了默认的 Email 内容,是否给你有一些启发?
其他
除了上述我简单演示的内容,Optional 还提供了很多方法,结合函数式编程的链式调用,可以非常潇洒和优雅的解决 NPE 问题。我这篇文章不是纯教学文章,而是希望引起你的思考,找到解决问题的思路最为重要,因为要去学习不如看官方文档,我写也不是权威的,所以读者应该学习和思考 JDK 大佬们的解决思维,在自己的项目中加以利用。
商业用途请联系作者获得授权。
版权声明:本文为博主「任霏」原创文章,遵循 CC BY-NC-SA 4.0 版权协议,转载请附上原文出处链接及本声明。
相关推荐
猜你还喜欢这些内容,不妨试试阅读一下评论与留言
以下内容均由网友提交发布,版权与真实性无法查证,请自行辨别。微信订阅号
扫码关注「任霏博客」微信订阅号- 你写得非常清晰明了,让我很容易理解你的观点。
- 感谢分享!拿走了~
- 您是说 UCClient 类接收来自Discuz的UCenter的消息吧,请求是来自 Discuz 的 UCenter吗?code 为 null 说明请求URL地址中没有 code 参数 (?code=xxx) ,确定是 UCenter 发起的请求吗?
- String code = request.getParameter("code"); code一直是null 这是为什么啊
- 你好,我想问一下如果是分析型的数据库要怎么制作docker镜像呢 是修改V008R003C002B0320版本号吗
- 可以的,我也正在开发分享的程序,可以邮件或群联系我都可以,关于页面里有联系方式:https://www.renfei.net/page/about 。
- 有破解软件的需要可以私下联系您吗?
- 您好,手机APP只是个客户端,用于数据呈现展示,数据均保存在服务器上,只留个APP没有任何用处,无能为力哦。
- 老哥 看你弄了这么多软件好厉害啊。 我有个软件 我买过几个小会员 没用几天 然后商家跑路了,软件服务器关闭了,连不上去 用不了。 你能做成一个打补丁版本可以本地用的么? 方便看下么?https://haodezhe.lanzouw.com/iD0f30h9joza 谢谢老哥!
- 您好,由于版权投诉和我国知识产权法的完善,我已经下架所有破解软件的下载链接了。
- 生花妙笔信手来 – 基于 Amazon SageMaker 使用 Grounded-SAM 加速电商广告素材生成 [1]
- github.renfei.net 不再完整代理 Github 页面改为代理指定文件
- 优雅的源代码管理(三):本地优雅的使用 Git Rebase 变基
- 优雅的源代码管理(二):Git 的工作原理
- 优雅的源代码管理(一):版本控制系统 VCS(Version Control System)与软件配置管理 SCM(Software Configuration Management)
- ChatGPT 开发商 OpenAI 买下极品域名 AI.com
- 火爆的 AI 人工智能 ChatGPT 国内注册教程、使用方式和收费标准
- 解决 SpringCloud 中 bootstrap.yml 不识别 @activatedProperties@ 参数
- Cron表达式书写教程搞定Linux、Spring、Quartz的定时任务
- 阿里云香港可用区C发生史诗级故障