Java之Optional使用姿势一
Optional类是Java8引入的一个解决空指针的解决方案,意图实现一种优雅的判空手段,帮助我们在开发的过程当中更优雅的去实现代码编写,写出简洁、健壮的代码。以下分别记录我在工作中遇到过的错误用法与我认为比较优雅的使用方法。
1、背景:
假设有一个学生类:Student;一个课程类:Course。显然是多对多的关系,一个学生有多门课程。一门课程被多个学生选择。那么有如下场景,学生在新学期开始时假设要选择选修课,那么会登录选课系统选择自己喜欢的课程,此时我们假设学生选完课程后点下确认按钮的时候前端封装了一个学生与课程的对应关系类,假设叫StudentCourseDTO。三个类如下:
Student:
package domain;
public class Student {
/**
* 学号
*/
private String sno;
/**
* 名字
*/
private String name;
}
Course:
package domain;
public class Course {
/**
* 编号
*/
private String id;
/**
* 名称
*/
private String name;
/**
* 学期数
*/
private Integer termNums;
}
StudentCourseDTO:
package dto;
import domain.Course;
import java.util.List;
public class StudentCourseDTO {
/**
* 学号
*/
private String sno;
/**
* 所选课程
*/
private List<Course> courses;
}
现在假设需要将学生选择的课程保存到数据库中,对应的Service取名StudentChooseCourseService,如下: StudentChooseCourseService:
package application;
import dto.StudentCourseDTO;
/**
* 学生选课服务,提供保存学生所选课程功能
*/
public class StudentChooseCourseService {
/**
* 保存学生所选课程
*
* @param studentCourseDTO 学生与课程对应DTO
*/
public void saveCourses(StudentCourseDTO studentCourseDTO) {
}
}
至此,选课功能的简单框架有了。
2、常规写法:
那么正常来讲,一般我们对于接口中的参数是不信任的,也许参数是空的,现在这种场景感受还不明显,但如果脱离这个场景,StudentCourseDTO是调用其它服务获取的,或者我们是从redis中查询的?这样的话明显我们是不信任这个DTO的,所以我个人喜欢防御性编程,对于外部的参数,我们一定要校验一下,比如说做基本的非空校验。所以一般会这么写:
package application;
import dto.StudentCourseDTO;
import java.util.Objects;
/**
* 学生选课服务,提供保存学生所选课程功能
*/
public class StudentChooseCourseService {
/**
* 保存学生所选课程
*
* @param studentCourseDTO 学生与课程对应DTO
*/
public void saveCourses(StudentCourseDTO studentCourseDTO) {
if (Objects.isNull(studentCourseDTO)) {
return;
}
}
}
乍看一下,没啥问题很正常,但是如果要保存课程列表,那还需要判断课程是空的吗,于是变成了这样:
package application;
import dto.StudentCourseDTO;
import java.util.Objects;
/**
* 学生选课服务,提供保存学生所选课程功能
*/
public class StudentChooseCourseService {
/**
* 保存学生所选课程
*
* @param studentCourseDTO 学生与课程对应DTO
*/
public void saveCourses(StudentCourseDTO studentCourseDTO) {
if (Objects.isNull(studentCourseDTO)) {
return;
}
if (Objects.isNull(studentCourseDTO.getCourses()) || studentCourseDTO.getCourses().size() == 0) {
return;
}
}
}
如此,又完成了一次校验。之后开始写我们真正的保存逻辑了。试想,现在是一个简单的StudentCourseDTO类,在日常开发中,会遇到很多复杂的嵌套,如果都需要校验一遍,那我们真正的核心逻辑很有可能相对于一堆校验来说变得微不足道。
3、使用Optional:
所以为了避免之后可能出现的空指针问题,使用Java8提供的Optional类。
但是,在工作中看到有人把Optional类用成了如下示例:
3.1、错误示例:
package application;
import dto.StudentCourseDTO;
import java.util.Optional;
/**
* 学生选课服务,提供保存学生所选课程功能
*/
public class StudentChooseCourseService {
/**
* 保存学生所选课程
*
* @param studentCourseDTO 学生与课程对应DTO
*/
public void saveCourses(StudentCourseDTO studentCourseDTO) {
if (!Optional.ofNullable(studentCourseDTO).isPresent()) {
return;
}
if (!Optional.ofNullable(studentCourseDTO.getCourses()).isPresent() || studentCourseDTO.getCourses().size() == 0) {
return;
}
}
}
这样虽然使用了Optional,但是效果和常规写法比较显得更加臃肿了,没有体现出Optional的好处。
Optional提供了类似Java中Stream流一般的使用方式。
3.2、正确姿势:
package application;
import dto.StudentCourseDTO;
import java.util.Optional;
/**
* 学生选课服务,提供保存学生所选课程功能
*/
public class StudentChooseCourseService {
/**
* 保存学生所选课程
*
* @param studentCourseDTO 学生与课程对应DTO
*/
public void saveCourses(StudentCourseDTO studentCourseDTO) {
Optional.ofNullable(studentCourseDTO)
.map(StudentCourseDTO::getCourses)
.ifPresent(courses -> {
// TODO: 2022/11/24 保存方法
});
}
}
这样的话就会比上面好很多,可读性也更强,写出来的代码更加简洁和优雅。当ofNullable判断studentCourseDTO是空的时候就不会走后面的代码了,且即使不为空,那么getCourses获取课程列表只有不为空的情况下才会执行后面的方法。
看,根本不会出现空指针:
转载自:https://juejin.cn/post/7169576664093949989