JDK8-Optional类避免NPE
程序员文章站
2022-06-07 12:35:59
...
1.需求分析
调用RPC返回结果、包装类转简单类型,拆箱过程最容易出现NPE问题。JDK8之前需要小心翼翼对各种类型进行判空,可通过JKD8-Optional类来预发未知的NPE错误。
2.关于避免NPE的常识
- 【强制】所有POJO类属性必须使用包装数据类型
- 【强制】所有RPC方法的返回和参数必须使用包装数据类型
- 【推荐】所有局部变量使用基本简单数据类型
-
Ps:
- POJO类属性没有初值是提醒使用者在需要使用时,必须自己显示地进行赋值,任何NPE问题,或者入库检查,都由使用者来保证。
- 正例:数据库的检查结果可能是null,因为自动拆箱,用基本数据类型接受有NPE风险。
- 反例:比如显示成交总额涨跌情况,即正负x%,x为基本简单类型,调用RPC服务,调用不成功时,返回的是默认值,页面显示为0%,显然这是不合理的,应该显示成-%。所以包装数据类型的null值,能够表示额外的信息,如:远程调用失败、异常推出。
- 【推荐】防止NPE,是程序员基本修养,注意NPE产生的场景。
- 1)返回类型为基本简单数据类型,return包装数据类型的对象时,自动拆箱有可能产生NPE。
- 反例: public int func(){ return Integer对象}; 如果为null,自动拆箱抛出NPE。
- 2)数据库的查询结果可能为null。
- 3)集合里的元素即使isNotEmpty,取出的元素仍有可能为null。
- 4)远程调用返回对象时,一律要求进行空指针判断,防止NPE。
- 5)对于Session中获取的数据,建议NPE检查,避免空指针。
- 6)级联调用obj.getA().getB().getC();一连串调用,易产生NPE。
- 正例:使用JDK8的Optional类防止NPE问题。
3.JDK8-Optinal类防止NPE
JDK8增加了许多有用的API,Optional类就是其中之一。如果不了解Optional类,只是简单的认为它可以解决NPE问题,于是就有了如下代码:
Optional<User> user = xxx
if(user.isPresent){
return user.getOrders();
}else {
return Collections.emptyList();
}
上面采用Optional类的代码和我们正常下面写法并没有太大区别:
User user = xxx
if(user!=null){
return user.getOrders();
}else{
return Collections.emptyList();
}
两者没有实质的区别,那么Optinal的正确使用姿势来了:
- 先来了解下Optional常用的方法有哪些?
1.public<U> Optional<U> map(Function<? super T,? extends U>mapper)
2.public T orElse(T other)
3.public T orElseGet(Supplier <? extends T> other)
4.public void ifPresent(Consumer <? super T> consumer)
5.public Optional <T> filter(Predicate <? supper T> predicate)
6.public <U> OPtional <U> flatMap(Function <? supper T,Optional <U> mapper>>)
7.public <X extends Throwable T> orElseThrow(Supplier <? extends X> exceptionSupplier) throws X
- Optional的三种构造方法:
- Optional.of(obj):要求传入obj不能为null,否则还没开始初始化就NPE
- Optional.ofNullable(obj):以一种智能、宽容的方式来构造一个Optional实例,来者不拒。传参为null时,就得到Optional.empty(),非null,则调用Optional.of(obj)
- Optional.empty():返回空obj
- 作者的观点:1.当我们非常非常确定要传给Optional.of(obj)的obj参数不可能为null时,比如它是一个刚new出来的对象(Optional.of(new User(…))),或者是一个非null的常量时。2.当想为obj断言不为null时,即我们想在玩意obj为null立即报告NPE异常,立即修改,而不是隐藏空指针异常时,我们就应该果断调用Optional.of(obj)来构造Optional实例,而不让任何不可预计的null值有可乘之机隐身于Optionl中。
-
存在即返回,无则提供默认值
//替代 return user.isPresent()?user.get:null;
return user.orElse(null);
return user.orElse(UNKNOW_USER);
//替代 return user.isPresent()?user:fetchAUserFromDatabase();
return user.orElse(()->fetchAUserFromDatabase());
- 存在才对它做点什么
user.ifPresent(System.out::printIn);
//替代下面
if(user.isPresent()){
System.out.printIn();
}
- user.isPresent()为true时,获得它关联的orders,为false时则返回一个空集合。那么我们用上面的orElse、orElseGet方法都乏力。这里可以采用map函数,只需要下面这一行代码:
return user.map(u->u.getOrders()).orElse(Collections.emptyList());
//替代下面繁琐的判断
if(user.isPresent()){
return user.get().getOrders();
}else{
retrun Collections.emptyList();
}
- map可能是无限级联的,比如多层,例如:
return user.map(u->u.getUserName()).map(name->name.toUpperCase()).orElse(null);
- JDK8之前的做法都是一层一层的展开,例如:
if(user!=null){
String userName=user.getUserName();
if(userName!=null){
return name.toUpperCase();
}else{
return null;
}
}else{
return null;
}
小结:使用Optional时尽量不要直接调用Optional.get()方法,Optional.isPresent()更应该被视为一个私有方法,应依赖于其他像:Optional.orElse(),Optional.orElseGet(),Optional.map()等方法。建议深入了解JDK8的源码。
参考地址:
阿里巴巴开发手册,
JDK8-Optional类解决NPE