前言
你是否在为控制台出现NullPointerException
而烦恼,是不是还在用一堆if
处理空值。JDK8重磅推出让你的空值处理从此没有if
,更加优雅,技术总监看了都说好的Optional
类。
它的作用不仅仅是在处理空值上面,它同时也会让程序员养成一种更好的习惯,在以前,程序员在调用一个方法的时候,极可能忘了处理返回值为null
的情况,如果返回值为空,极大可能会出现空指针异常。
如果你调用一个方法,返回值是Optional
,那么程序员马上就会意识到这个方法可能会返回空值,并且Optional
类的API也促使程序员去处理空值情况,从另一个角度提高了程序健壮性。
看到这里你应该理解了Optional
类存在的意义以及我们平时应该在哪些场景使用Optional
类了。
1. 方法介绍
现在我们来学习下Optional
类的一些API,根据这些方法的特点可以分为下面几类:
- 创建Optional对象
方法 | 描述 |
---|---|
Optional.of(T value) | 创建一个包含给定非 null 值的 Optional |
Optional.ofNullable(T value) | 创建一个值可能为 null 的 Optional |
Optional.empty() | 创建一个没有值的 Optional |
- 查询方法
方法 | 描述 |
---|---|
isPresent() | 如果值存在返回 true ,否则返回 false |
isEmpty() | 如果值不存在返回 true ,否则返回 false (Java 11新增) |
- 获取值
方法 | 描述 |
---|---|
get() | 如果值存在,返回该值,否则抛出 NoSuchElementException |
orElse(T other) | 如果值存在,返回该值,否则返回 other |
orElseGet(Supplier<? extends T> other) | 如果值存在,返回该值,否则调用 Supplier 获取默认值 |
orElseThrow(Supplier<? extends X> exceptionSupplier) | 如果值存在,返回该值,否则调用 Supplier 获取异常并抛出 |
- 转换和链式操作
方法 | 描述 |
---|---|
map(Function<? super T,? extends U> mapper) | 如果值存在,使用给定的映射函数处理并返回一个新的 Optional |
flatMap(Function<? super T,Optional<U>> mapper) | 与 map 类似,但映射函数的结果直接是一个 Optional |
filter(Predicate<? super T> predicate) | 如果值存在并满足给定的谓词,则返回描述该值的 Optional,否则返回一个空的 Optional |
- 其他方法
方法 | 描述 |
---|---|
ifPresent(Consumer<? super T> action) | 如果值存在,则使用该值调用指定的消费者 |
ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) | 如果值存在,使用该值调用指定的消费者,否则执行给定的 Runnable(Java 9新增) |
2. 示例代码
在编写一些简单示例之前,我们来回顾一下之前一个示例的部分代码:
Optional<Student> maxScoreStu = stuList.stream() // 获取Stream流
.max(Comparator.comparingInt(Student::getScore)); // 直接使用终止操作 max得到结果
maxScoreStu.ifPresent(System.out::println); // Optional为控制处理提供了更优雅的方式
我们使用终止操作max
来获取班级中成绩最高的学生的信息,它返回的是Optional<Student>
类型的数据,我们调用ifPresent
方法,传入符合Consumer
函数式接口的Lambda表达式来消费Student对象。
接下来我们写几个简单的示例,来体验一下Optional
类的具体使用:
2.1 示例一
我们现在向封装一个工具函数,用来把字符串转换为数字,我们使用Optional来处理转换异常的情况
/**
* 自定义把字符串转换为数字的方法
* @param str 字符串
*/
public static Optional<Integer> parseStringToInt(String str){
try {
return Optional.of(Integer.parseInt(str));
}catch (NumberFormatException e){
return Optional.empty();
}
}
/**
* 简单的场景,使用Optional来处理一些异常情况
* 弱化异常处理,强化业务逻辑
*/
@Test
public void demo1(){
Scanner in = new Scanner(System.in);
Optional<Integer> numOpt;
do {
System.out.print("请输入整数:");
numOpt = parseStringToInt(in.next());
}while(!numOpt.isPresent());
// 获取用户输入的数字
System.out.println("你输入的整数是:"+numOpt.get());
}
我们的工具方法中,转换成功会返回包含期望值的Optional
类,如果转换失败,会在异常处理中返回空的Optional
类,这样在调用这个方法的时候不关心异常处理,只关心有没有转换成功,通过Optional
提供的方法我们可以很轻松的处理这些逻辑。
所有可能返回空值的方法,我们都可以采用Optional
类作为返回类型,能在一定程度上提高程序的健壮性。
2.1 示例二
我们在平时开发中肯定会遇到很多返回值不是Optional的函数,但是返回值又可能是空值,所以我们还有另一种通用的用法来使用Optional来处理空值。
假设我们有一个业务方法返回一个Integer
值
/**
* 模拟的业务方法,获取一个整数
*
* @param arg 参数
*/
public static Integer getSomething(int arg) {
return arg % 2 == 0 ? null : 1;
}
我们在调用它的时候需要考虑到它的返回值为空的情况,假设为空的时候我们需要给它赋一个默认值,那么我们可以这样写代码
@Test
public void demo2() {
// 调用业务方法拿到返回值
Integer res = getSomething(2);
// 使用Optional来实现空值判断
Integer result = Optional.ofNullable(res)
.filter(item -> item > 0)
.orElse(1);
System.out.println("结果是:" + result);
}
使用Optional
的ofNullable
方法来用一个可能为空的值创建一个Optional
对象,然后调用filter
方法来判断值是否大于0,这个是我们假定的业务逻辑,假设业务中有这个需求,然后我们就要去获取这个值。如果是空的,那么会返回我们填入的1。
总结
Optional这个类让我们从重复又枯燥的空值判断中解脱出来,用另一种更为优雅和符合逻辑的方式来处理Java开发中可能遇到的空值情况。Optional本身API的风格也让代码阅读起来更容易理解,提高代码健壮性的同时也提高了代码的可读性。
轻松使用Optional类的前提是学会函数式接口和Lambda表达式,所以大家掌握这两个东西了吗,如果没有掌握记得看我之前的文章,我们下篇文章见!