⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 潘志的研发笔记 「志哥聊技术」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

一、背景介绍

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的原因啦!

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

二、案例实践

在 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)

三、小结

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

另外orElse、orElseGet、orElseThrow区别如下:

  • orElse:如果实例包含空值,返回传入指定的值
  • orElseGet:如果实例包含空值,返回传入的方法中返回值
  • orElseThrow:如果实例包含空值,返回指定的异常类型

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

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

文章目录
  1. 1. 一、背景介绍
  2. 2. 二、案例实践
    1. 2.0.1. 2.1、empty()
    2. 2.0.2. 2.2、of()
    3. 2.0.3. 2.3、ofNullable()
    4. 2.0.4. 2.4、isPresent()
    5. 2.0.5. 2.5、get()
    6. 2.0.6. 2.6、ifPresent()
    7. 2.0.7. 2.7、filter()
    8. 2.0.8. 2.8、map()
    9. 2.0.9. 2.9、flatMap()
    10. 2.0.10. 2.10、orElse()
    11. 2.0.11. 2.11、orElseGet()
    12. 2.0.12. 2.12、orElseThrow()
  • 3. 三、小结