Optional类是Java8新增的一个类,其作用可以有效避免空指针异常。
Optional类的代码比较简单,很容易就能看懂,其实质就是一个辅助类,把需要处理的对象做了一层包装,然后再使用Optional中的方法时,可以有效得判断处理对象是否为空,从而做出正确判断。
接下来我们看下如何使用Optional。
创建Optional
创建Optional有3种方式:
- Optional.empty() 返回一个空的Optional
- Optional.of(不为null的对象)
- Optional.ofNullable(可以为null的对象)
如果能够确保入参一定不为null,可以用Optional.of
,因为在Optional.of
内部会判断是否为null,如果是null则抛出异常。
如果不太确定入参是否为null,可以用Optional.ofNullable
。
对象创建好了,接下来看看如何使用。
isPresent和ifPresent
isPresent()方法判断处理对象是否为null,不为null返回true,源码如下:
public boolean isPresent() {
return value != null;
}
ifPresent方法有一个入参ifPresent(Consumer super T> consumer)
,它的意思是如果对象不为null,则运行consumer进行处理,有点类似回调函数。
String s = "hello";
Optional optional = Optional.of(s);
if(optional.isPresent()) {
System.out.println("the value is " + optional.get());
}
同样可以写成:
optional.ifPresent((val) -> {
System.out.println("the value is " + val);
});
filter
filter是对处理对象进行判断,如果判断为true,则返回当前Optional,如果为false则返回一个空的Optional对象,其源码如下:
public Optional filter(Predicate super T> predicate) {
Objects.requireNonNull(predicate);
if (!isPresent())
return this;
else
return predicate.test(value) ? this : empty();
}
filter方法有个参数:Predicate,这是一个函数式接口,因此我们可以使用Lambda表达式来处理。
String s = "hello";
Optional optional = Optional.of(s);
boolean exist = optional
.filter(val -> "hello1".equals(val))
.isPresent();
System.out.println(exist); // false
map和flatMap
map方法的作用可以简单理解成从处理对象中取出其它对象,然后返回一个新的Optional。如下代码所示:
public class OptionalMapTest {
static class Goods {
private String goodsName;
private Company company;
...getter setter
}
static class Company {
private String companyName;
...getter setter
}
public static void main(String[] args) {
Company company = new Company();
company.setCompanyName("Apple");
Goods goods = new Goods();
goods.setGoodsName("iphoneX");
goods.setCompany(company);
Optional optional = Optional.of(goods);
String companyName = optional
// 从goods中取出Company,返回一个新的Optional
.map(goodsObj -> goodsObj.getCompany())
// 从company中取出companyName,返回一个新的Optional
.map(companyObj -> companyObj.getCompanyName())
// 得到companyName
.get();
System.out.println(companyName);
}
}
什么情况下该使用flatMap呢,我们把Goods中的的Company对象改成Optional
。
static class Goods {
private String goodsName;
private Optional company;
...getter setter
}
此时下面这段代码会编译报错
String companyName = optional
// 从goods中取出Company,返回一个新的Optional
.map(goodsObj -> goodsObj.getCompany()) // !!这里会报错
// 从company中取出companyName,返回一个新的Optional
.map(companyObj -> companyObj.getCompanyName())
// 得到companyName
.get();
主要是这行代码optional.map(goodsObj -> goodsObj.getCompany())
。因为此时返回的是一个Optional
对象。
而我们需要的是Optional
对象,这个时候就应该用到flatMap了,只要把optional.map(goodsObj -> goodsObj.getCompany())
改成optional.flatMap(goodsObj -> goodsObj.getCompany())
即可。
String companyName = optional
// 从goods中取出Company,返回一个新的Optional
.flatMap(goodsObj -> goodsObj.getCompany())
// 从company中取出companyName,返回一个新的Optional
.map(companyObj -> companyObj.getCompanyName())
// 得到companyName
.get();
简单的理解就是:
- optional.map() 会把返回的结果再次放到一个Optional中
- optional.flatMap() 不会把结果放入放到Optional中,把这个操作交给开发者来处理,让开发者自己返回一个Optional
get,orElse,orElseGet,orElseThrow
- get():返回被处理的值,如果值为空,则抛出异常
String s = null;
Optional optional = Optional.ofNullable(s);
System.out.println(optional.get()); // 抛出java.util.NoSuchElementException: No value present
针对这种情况,有几种处理方式
方式1:使用isPresent()
String s = null;
Optional optional = Optional.ofNullable(s);
if (optional.isPresent()) {
System.out.println(optional.get());
} else {
System.out.println("默认值");
}
方式2:使用orElse(默认值)
String s = null;
Optional optional = Optional.ofNullable(s);
System.out.println(optional.orElse("默认值"));
orElse(默认值)的意思是如果Optional中的值为null,则返回给定的默认值。
方式3:使用orElseGet(Supplier)
String s = null;
Optional optional = Optional.ofNullable(s);
System.out.println(optional.orElseGet(() -> "默认值"));
orElse(Supplier)的意思是如果Optional中的值为null,则执行指定的Supplier接口,由于Supplier是个函数式接口,因此可以使用Lambda表达式代替。
由此看来,方式2和方式3的处理是比较优雅的。
方式2和方式3的区别在于,方式3可以延迟返回,只有值为null的情况下才会触发() -> "默认值"
,从而避免生成无用对象,方式2不管如何都生成了"默认值"这个字符串对象。下面的例子可以说明:
String s = "1";
Optional optional = Optional.ofNullable(s);
System.out.println(optional.orElse(getDefault()));
打印:
生成了字符串对象
1
即使Optional中的值不为null,但还是执行了getDefault(),这完全没必要,再来看下使用orElseGet
String s = "1";
Optional optional = Optional.ofNullable(s);
System.out.println(optional.orElseGet(() -> getDefault()));
打印:1
接着再看下orElseThrow,如果值为null,则直接抛出异常
String s = null;
Optional optional = Optional.ofNullable(s);
System.out.println(optional.orElseThrow(() -> new NullPointerException("不能为空")));
Optional实战
{
"user": {
"age": 20
,"name": "Jim"
,"address": {
"province": "浙江省"
,"postcode": "111111"
}
}
}
假设有这样一个json字符串,现在要获取postcode信息。如果不用Optional的话,要写各种if…else语句,还要判断字段是否存在。
String postcode = "unknown";
JSONObject user = jsonObj.getJSONObject("user");
if (user != null) {
JSONObject address = user.getJSONObject("address");
if (address != null) {
String code = address.getString("postcode");
if (postcode != null) {
postcode = code;
}
}
}
System.out.println(postcode);
但是用Optional可以这样写:
JSONObject jsonObj = JSON.parseObject(json);
String postcode = Optional.ofNullable(jsonObj)
.flatMap(jsonObject -> Optional.ofNullable(jsonObject.getJSONObject("user")))
.flatMap(jsonObject -> Optional.ofNullable(jsonObject.getJSONObject("address")))
.flatMap(jsonObject -> Optional.ofNullable(jsonObject.getString("postcode")))
.orElse("unknown");
System.out.println(postcode);
注意,这里要使用flatMap,由开发者手动返回一个Optional对象,如果使用map的话则返回Optional
。
最后一句.orElse("unknown")表示如果一路走下来没有找到值,则返回一个默认值。
Optional的优势是处理嵌套数据结构,如这里的json数据。假如这段json数据结构不是完整的,postcode字段不存在,或者整个address字段都不存在,在无法保证嵌套数据中的值是否存在的情况下,使用Optional是个不错的选择。它都能确保有个正确的返回值。
小节
本篇主要介绍了Optional类的用法,同时演示了如何使用Optional处理嵌套数据。
定期分享技术干货,一起学习,一起进步!