在开发过程中,经常会遇到判空的情形,若是不处理很有可能出现异常。但是进行的判空代码又冗余,且有时候出现级联判空的时候代码不太优雅。而jdk8中的Optional类正在处理空指针异常的新增类。
Optional类可以理解为一个容器,它可以存null,也可以存非null对象。
1、Optional类的基本使用
//创建2个学生对象,一个为非null,一个为null
Student student1 = new Student();
student1.setAge("26");
student1.setCity("wuhan");
student1.setSName("qsm");
Student student2 = null;
//创建Optional类对象
Optional<Student> optionalStudent1 = Optional.of(student1);
//Optional<Student> optionalStudent1 = Optional.of(student2); //报错:java.lang.NullPointerException
Optional<Student> optionalStudent2 = Optional.ofNullable(student2);//Optional.empty
//直接获取
System.out.println(optionalStudent1.get());//Student(sName=qsm, identityNum=null, age=26, city=wuhan)
//System.out.println(optionalStudent2.get());//报错:java.util.NoSuchElementException: No value present
//判空获取或者说是条件获取
//orElse是为空则返回后面逻辑的返回值;orElseGet一样,只不过是方法里可以写其他逻辑;orElseThrow,空则直接返回异常
System.out.println(optionalStudent1.orElse(new Student()));//Student(sName=qsm, identityNum=null, age=26, city=wuhan)
System.out.println(optionalStudent2.orElse(new Student("hn")));//Student(sName=hn, identityNum=null, age=null, city=null)
System.out.println(optionalStudent1.orElseGet(new Supplier<Student>() {
@Override
public Student get() {
return new Student("liuqiangdong");
}
}));//Student(sName=qsm, identityNum=null, age=26, city=wuhan) 下面一种方法为lambda简写
System.out.println(optionalStudent1.orElseGet(() -> new Student("liuqiangdong")));//Student(sName=qsm, identityNum=null, age=26, city=wuhan)
System.out.println(optionalStudent2.orElseGet(() -> new Student("mayun")));//Student(sName=mayun, identityNum=null, age=null, city=null)
System.out.println(optionalStudent1.orElseThrow(BizException::new));//Student(sName=qsm, identityNum=null, age=26, city=wuhan)
System.out.println(optionalStudent1.orElseThrow(()-> new BizException("3000","该学生为null")));//Student(sName=qsm, identityNum=null, age=26, city=wuhan)
//System.out.println(optionalStudent2.orElseThrow(BizException::new));//Exception in thread "main" com.jd.ins.qsm.demo.web.commom.excption.BizException
// filter()和 map()
Student student3 = new Student();
student3.setAge("18");
student3.setCity("beijing");
student3.setSName("hn");
List<Student> studentList = Lists.newArrayList();
studentList.add(student1);
studentList.add(student3);
// filter(),如果 Optional 对象有值且满足条件则返回原来的Optional 对象,否则返回空的Optional对象
Optional<List<Student>> optionalStudentList1 = Optional.ofNullable(studentList);
System.out.println(optionalStudentList1.filter(tempOptionalStudentList -> tempOptionalStudentList.size() > 1).orElse(Lists.newArrayList()));
//[Student(sName=qsm, identityNum=null, age=26, city=wuhan), Student(sName=hn, identityNum=null, age=18, city=beijing)]
Optional<List<Student>> optionalStudentList2 = Optional.ofNullable(null);
System.out.println(optionalStudentList2.filter(tempOptionalStudentList -> tempOptionalStudentList.size() > 1).orElse(Lists.newArrayList()));//[]
//map(), 如果Optional对象的值存在,执行 mapper 映射函数,返回函数返回的新的optional对象,否则返回空的Optional对象
Optional<List<Student>> students = optionalStudentList1.map(tempOptionalStudentList -> {
for (Student student : tempOptionalStudentList) {
student.setAge("99");
}
return tempOptionalStudentList;
});
System.out.println(students.get());//[Student(sName=qsm, identityNum=null, age=99, city=wuhan), Student(sName=hn, identityNum=null, age=99, city=beijing)]
System.out.println(optionalStudent1.map(student -> student.getAge()).get());//99
//如果存在ifPresent
optionalStudent1.ifPresent(new Consumer<Student>() {
@Override
public void accept(Student student) {
student.setIdentityNum("1234");
}
});
System.out.println(optionalStudent1.get());//Student(sName=qsm, identityNum=1234, age=99, city=wuhan)
optionalStudent2.ifPresent(student -> student.setIdentityNum("456"));//不会执行
//System.out.println(optionalStudent2.get());//java.util.NoSuchElementException: No value present
2、实际使用
在实际开发中,经常遇到2种情形:
第一个是级联判空经常出现;第二个某字段必须存在值,否则需要报错。
下面例子中,老师类里面有一个地址类的字段
private static void compare() throws Throwable {
Address address = new Address();
address.setCity("wuhan");
Teacher1 teacher1 = new Teacher1();
teacher1.setTName("qsm老师");
teacher1.setAddress(address);
//原来的级联判空
if (ObjectUtils.isNotEmpty(teacher1) && ObjectUtils.isNotEmpty(teacher1.getAddress())) {
System.out.println(teacher1.getAddress().getCity());//wuhan
}
//使用Optional
//下面为简写,与上面一致
Optional.ofNullable(teacher1).map(Teacher1::getAddress).map(Address::getCity).orElse(null);//wuhan
//Optional.ofNullable(teacher1).map(tempTeacher -> tempTeacher.getAddress()).map(tempAddress -> tempAddress.getCity()).orElse(null);
//若程序中某个字段必须非空,否则需要抛出异常
//原来方法
if (ObjectUtils.isEmpty(teacher1) || ObjectUtils.isEmpty(teacher1.getAddress()) || ObjectUtils.isEmpty(teacher1.getAddress().getProvince())) {
throw new BizException("32", "省份不存在");
}
Optional.ofNullable(teacher1).map(Teacher1::getAddress).map(Address::getProvince).orElseThrow(() -> new BizException("32", "省份不存在"));
//com.jd.ins.qsm.demo.web.commom.excption.BizException: 省份不存在
}
还有一种使用方法可以点击查看
Optional联合Stream使用——集合判空或取值
3、map和flatMap区别
简单而言:map的返回值会自动被包装为Optional对象。而flatMap则需要手动封装。具体请参考JDK8的Optional类中map方法和flatMap方法的区别
4、思考
Optional可以有效的解决空指针异常的问题。
同时也有一些问题需要注意,第一个是代码量并没有很有效的减少,第二个是判空也只是null的判空,对于字符串的空字符,集合的空元素等有待优化。
总体而言,Optional可以作为辅助手段进行代码的编写。
【正在去BAT的路上修行!!!】