优雅解决空指针问题——Optional类的使用
前言
空指针是java程序员在工作中遇到的最多的异常之一,对于对象中的某个属性,有时候我们为了避免程序报空指针错误,而不得不使用较多的
if
、else
来进行逻辑判断,但这样的话代码可能就会比较冗余或者说不够优雅。在JDK1.8中,提供了Optional
类为我们解决空指针问题提供了一种方式,有需要的同学可以参考一下本篇文章。
先讲一个小例子
假如我们想获取Student类中省份信息,我们需要执行的代码如下:
这种方法存在空指针的隐患,当出现
Address
为空甚至是Student
对象为空的情况,此时我们就不得不通过条件判断语句来避免空指针的产生;而使用Optonal
后解决方式如下:
public class OptionalTest {
public static void main(String[] args) {
Student student = new Student("小明",16,null);
String result = new OptionalTest().OptGetProvince(null);
System.out.println(result);
}
public String OptGetProvince(Student student){
return Optional.ofNullable(student)
.map(s -> s.getAddress())
.map(a -> a.getProvince())
.orElse("none");
}
}
可以看到,通过Optional
的使用,使得整体的判断变得十分的清爽简洁。
讲讲我对Optional类的理解:我们可以把该类看成是一个容器,我们将对象存储到容器中后,通过调用内置的api,可以较为安全地过滤掉可能存在的空指针问题,避免繁琐的if、else操作。让我们的代码尽可能的简洁
Optional的常见API
(一)构造函数: empty,of,ofNullable
1. empty返回一个空的Optional对象
Optional.empty();
2. of根据传入的值生成Optional
对象
// 方式2 将非空对象作为属性传入Optional类中
Student s = new Student("小明",16);
Optional.of(s.getAddress());
3. ofNullable 和of方法一样,根据传入的值生成optional
对象
// 方式3 将非空对象作为属性传入Optional类中
Student s = new Student("小明",16);
Optional.ofNullable(s.getAddress());
我们可以发现,of
和ofNullable
的作用很相近,实际上,进入Optional
类的源代码看的话,可以发现对于ofNullable
方法的话是有进行判空的。也就是说,如果使用of
方法传入的参数是null
,同样会报空指针!!!
(二)值选择方法:orElse
,orElseGet
和orElseThrow
上述的三个方法相当于是SQL
中的NVL
函数,若Optional
中值为null,则给定一个默认值。
1、orElse
Student s = new Student("小明",16,new Address());
String result = Optional.ofNullable(s.getAddress().getProvince()).orElse("北京");
2、orElseGet
Student s = new Student("小明",16,new Address());
String result = Optional.ofNullable(s.getAddress().getProvince()).orElseGet(()->"北京");
3、orElseThrow
Student s = new Student("小明",16,new Address());
String s3 = Optional.ofNullable(s.getAddress().getProvince()).orElseThrow(() -> new IllegalArgumentException("缺少参数"));
我们可以发现,对于orElseThrow
和orElseGet
两个方法,是采用函数式接口的方式来作为参数的。
同时,对于orElse
和orElseGet
两个方法,作用相近,具体有哪些不同呢?
答案是若Optional对象中的值不为空,则orElseGet不会创建参数中的对象,而orElse无论什么情况都会创建参数对象。
Optional.ofNullable(new Student("小明",16)).orElseGet(()->new Student("小红",17));
上面这条式子,会创建两个student对象
(三)值转换函数:map和flagMap
值转换的意思是对Optional
对象中的value
值进行转换,对值应用(调用)作为参数的函数,然后将返回的值包装在 Optional
中
1、map
Student student = new Student("小明",16,new Address());
String s1 = Optional.ofNullable(student).map(s -> s.getName()).get();
2、flagMap
Student student = new Student("小明",16,new Address());
String s1 = Optional.ofNullable(student).flatMap(s -> s.getName()).get();
两个函数都可以实现值的转换,那么两者的区别在哪呢?答案是二者的入参不同
以上面的flagMap的示例代码为例,我们需要在Student类中重写一下getName方法,使其返回
Optional
对象
public class Student {
private String name;
public Optional<String> getName() {
return Optional.ofNullable(this.name);
}
}
(四)判空函数:isPresent和ifPresent
两个函数的用法类似,都可以用作判空,区别在于当不为空时,ifPresent
会执行对应的函数。
1. isPresent
Student student = new Student("小明",16,new Address());
boolean b1 = Optional.ofNullable(student.getAddress()).isPresent();
System.out.println(b1); // true
2. ifPresent
Student student = new Student("小明",16,new Address());
Optional.ofNullable(student.getAddress()).ifPresent(address -> System.out.println(address));
(五)过滤(筛选)函数:filter
该函数的作用是,判断Optional
中的值是否满足指定条件,若满足则返回,否则返回一个EMPTY
对象。
Student student = new Student("小明",16,new Address());
Student result = Optional.ofNullable(student).filter(s -> s.getName().equals("小红")).orElseGet(() ->new Student("小蓝",10));
System.out.println(result); // Student{address=null, name='小蓝', age=10}
这里会筛选出满足姓名为小红的Student
对象,若不满足则新建一个姓名为小蓝的Student
对象。
到这里,关于Optional类的常用api已经介绍完毕,相信大家通过这篇文章可以了解该类的作用和使用,要想熟练地进行应用,还需要大家在日常开发中的使用。
下面再总结一些常见的优化场景
优化前
public String originTest(Student student){
if(null != student){
Address address = student.getAddress();
if(null != address){
String province = address.getProvince();
if(null != province){
return province;
}
}
}
return null;
}
优化后
public String optimizeTest(Student student) {
return Optional.ofNullable(student)
.map(s -> s.getAddress())
.map(a -> a.getProvince())
.orElse("none");
}
优化前
public void originTest(Student student){
if(null != student){
// dosomething
}
}
优化后
Optional.ofNullable(student)
.ifPresent(s -> doSomeThing);
写在最后
Optional
是Java8推出的一个小特性,在一些get语句嵌套比较多的场景,比较适合运用,学习成本也不算高(其源码比较简单),但也需要注意,使用Optonal在简洁化代码的同时,相对来说会损失一定的代码可读性,具体的使用也要开发人员在实际场景中加以权衡后使用。
个人建议的话,哪怕是自己不使用也要尽量掌握,避免出现阅读源码的时候出现障碍。
参考文章:
1、Java中Optional类的使用 https://blog.csdn.net/wwe4023/article/details/80760416
2、JAVA8之妙用Optional解决判断Null为空的问题 http://www.ibloger.net/article/3209.html