关于Option的介绍可以看深入理解java8中的Optional 类就可以了,但是复杂一点的使用在网上却没有搜到,这里结合我开发时遇到的真实案例来讲一下Option的使用。
public class PictureCondition {
private String url;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
在这里先一个简单的对象,里面也就只有一个属性,在Controller层需要接收一个PictureCondition的对象,应判断这个对象里面的url属性是否为空,或者是有空格出现,如果有的话,则抛出一个异常。
使用一般的写法是这样判断的:
if(pictureCondition==null||pictureCondition.getUrl()==null||"".equals(pictureCondition.getUrl().trim())){
throw new RuntimeException("出错啦");
}
使用Option的写法是这样的:
Optional.ofNullable(pictureCondition)
.map(PictureCondition::getUrl)
.filter(x -> !"".equals(x.trim()))
.orElseThrow(() -> new RuntimeException("出错啦"));
对于单层循环使用一般的写法还是可以应对的,但是当层数比较多时,这样写就不太好了。
当 user!=null为真, 获得它关联的 orders的映射集合, 为假则返回一个空集合时, 我们用上面的 orElse, orElseGet 方法都乏力时, 那原本就是 map 函数的责任, 我们可以这样一行
return user.map(u -> u.getOrders()).orElse(Collections.emptyList())
//上面避免了我们类似 Java 8 之前的做法
if(user!=null && user.getOrders!=null) {
return user.getOrders();
} else {
return Collections.emptyList();
}
map 是可能无限级联的, 比如再深一层, 获得用户名的大写形式
return user.map(u -> u.getUsername())
.map(name -> name.toUpperCase())
.orElse(null);
这要搁在以前, 每一级调用的展开都需要放一个 null 值的判断
User user = .....
if(user != null) {
String name = user.getUsername();
if(name != null) {
return name.toUpperCase();
} else {
return null;
}
} else {
return null;
}
对于Optional对象中map和flatMap的区别:
map会将传入的Function函数的结果进行封装,先看源码:
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Optional.ofNullable(mapper.apply(value));//会使用Optional的ofNullable方法包装Function函数返回的值
}
}
flatMap会直接返回Function函数执行的结果,看源码:
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Objects.requireNonNull(mapper.apply(value));//直接返回Function执行的结果
}
}
这样看,好像也看不出来两者太大的区别,不知道什么时候用map,什么时候用flatMap,首先对于下列对象的操作,
public class School {
private String name;
private Optional<Tearch> tearch; //属性为Option封装的Tearch对象
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Optional<Tearch> getTearch() {
return tearch;
}
public void setTearch(Optional<Tearch> tearch) {
this.tearch = tearch;
}
}
class Tearch{
private String name;
private Optional<Student> student;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Optional<Student> getStudent() {
return student;
}
public void setStudent(Optional<Student> student) {
this.student = student;
}
}
class Student{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
这时,如果给你一个School对象,让你得到Student的name属性的值,可以使用下面的方式:
public static String getStudentName(School school){
return Optional.ofNullable(school)
.map(School::getTearch)
.map(Tearch::getStudent)
.map(Student::getName)
.orElse("false");
}
你可能感觉这样写很对啊,没毛病,可惜这段代码连编译都不会通过的,我们思考一下,School::getTearch会返回School实例的tearch属性,而tearch属性是使用Optional包装的Tearch对象,所以使用了map(School::getTearch),会返回Optional<Optional<Tearch>>对象,而不是我们所想的Optional<Tearch>,这是你可能想自己试一下,我已经做了截图,毕竟有图有真相。
这时就可以使用flatMap来解决这个问题,刚才已经说了,flatMap不会使用Optional包装Function执行的返回结果,所以我们可以使用flatMap来解决这个问题。
public static String getStudentName(School school){
return Optional.ofNullable(school)
.flatMap(School::getTearch)
.flatMap(Tearch::getStudent)
.map(Student::getName).orElse("false");
}
看截图:
这时map和flatMap的用法就清楚,如果某对象实例的属性本身就为Optional包装过的类型,那么就要使用flatMap方法,就像School::getTearch返回的就是Optional<Tearch>类型的,所以不用再使用Optional进行包装,这时就要选用flatMap方法,对于返回值是其他类型,需要Optional进行包装,如Student::getName得到是String类型的,就需要使用map方法在其外面包装一层Optional对象。
注意:Option类是判断空指针异常时用的,对于其他的if..else只要不是判断空指针的问题,就不要使用Option类,lz就陷入了一个误区,以为学了Option类,看见if...else就往Option来想,这么做真是大作特错。用Option的情况时在判断值是否为null时,Option就发挥作用了。