前言
如果你还没有使用过Optional,建议直接从正文开始食用。而对于使用过的Optional同学,可以思考下面四个问题。
1:Optional为什么会出现,解决了什么问题?
2:Optional是为了优化空指针异常,那使用Optional的过程中会不会抛出空指针异常?
3:Optional中Map方法与flatMap方法的区别与联系以及各自的适用场景?
4:Optional的适用场景,是否可以用作对象属性类型吗?方法入参类型?方法返回参数类型?
正文
A container object which may or may not contain a non-null value
Optional作为JDK1.8引入的一个新特性,官方介绍Optional是一个判断对象是否为空的容器。虽然Optional的代码只有350行(还包括大量的注释),但是功能却不简单,算得上小巧而又精悍。在Optional出现之前我们是如何判断为空呢。
if(null == object){...}
相信这段代码大家再熟悉不过了。
假设现在有一个需求,利用下面给出的json串在控制台打印博主的体重。
{
"1":{
"name":"bozhu",
"body":{
"height":180,
"weight":78
},
"marriage":"single"
}
}
我可以这么写
版本一
String weight = JSONObject.parseObject(json).getJSONObject("1")
.getJSONObject("body")
.getString("weight");
但是大家会发现这段代码极易报错,因为你并不确定json串中一定会包含body或者weight信息。每一次调用JSONObject#get方法都有可以抛出空指针异常。既然如此,我们需要在每调用一次JSONObject#get方法之后进行判空,修改后的代码是这样的。
版本二
JSONObject bozhuJson = JSONObject.parseObject(json).getJSONObject("1");
if(null!= bozhuJson) {
JSONObject bodyJson = bozhuJson.getJSONObject("body");
if (null != bodyJson) {
String weight = bodyJson.getString("weight");
}
}
当你看到这段代码你一定会觉得很糟糕,因为当对象的属性层级愈来越深,你需要写更多的判空代码。这部分工作无聊且枯燥,而且一旦因为某个判空逻辑漏写,根据墨菲定律,在未来某个时间这段疏忽的代码一定会报错。尽管你可以在上线前进行充分的测试,你没法保证测试可以覆盖到每处细节,而且这也很浪费资源。
这时候Optional就像一束光出现了,利用Optional修改后的代码是这样的。
版本三
String weight = Optional.ofNullable(JSONObject.parseObject(json))
.map(jsonObject -> JSONObject.parseObject(jsonObject.getString("1")))
.filter(jsonObject -> "bozhu".equals(jsonObject.get("name")))
.map(jsonObject -> JSONObject.parseObject(jsonObject.getString("body")))
.map(jsonObject -> jsonObject.getString("weight"))
.orElse("70");
我们可以发现版本三的代码和版本一的代码有一点点像,但是不同之处在于利用了Optional容器包装了Json对象。在Optional的相应方法的内部都进行了判空校验,如果Optional容器的对象不为空,会执行方法传入的Lambda表达式,否则不执行。
以Optional#map方法为例。
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
//1:判断mapper不为空
Objects.requireNonNull(mapper);
//2:判断Optional容器中的对象不为空
if (!isPresent())
return empty();
else {
//3:如果Optional容器中对象不为空,执行mapper。
//利用mapper的执行结果生成一个Optional对象。
return Optional.ofNullable(mapper.apply(value));
}
}
在Optional中还存在一个flatMap()方法,与Optional#map不同之处在于接收的参数mapper必须是一个Optional对象。Optional#flatMap与map方法都会返回一个Optional对象,但是每次调用map方法都会生成一个新的Optional对象,而flatMap方法会返回传入参数中被重新赋值的Optional对象。
还有一个不同点在于对于map方法而言当传入的mapper对象为空时,抛出NullPointerException异常。而对于flatMap方法而言,mapper对象为空或者mapper对象的执行结果为空时,抛出NullPointerException异常。
/**
*This method is similar to {@link #map(Function)},
* but the provided mapper is one whose result is already an {@code Optional},
* and if invoked, {@code flatMap} does not wrap it with an additional
* {@code Optional}.
*/
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
//1:判断mapper不为空
Objects.requireNonNull(mapper);
//2:判断Optional容器中的对象不为空
if (!isPresent())
return empty();
else {
//3:当Optional容器中对象不为空,执行mapper的函数代码。
//如果mapper执行结果不为空将其返回,一个重新赋值的Optional对象。
return Objects.requireNonNull(mapper.apply(value));
}
}
值得注意的是Optional没有实现Serializable接口,因此Optional不可以用作对象属性的类型。同时不推荐用作函数的参数类型,但是可以用作函数的返回类型。
Optional的代码逻辑十分简单,因此不做额外的阐述。如果有问题可以在评论区询问。