简述一下对Java8中Optional函数编程的认识-如何优雅的解决NPE异常
为什么会有这篇文章
JDK 8发布后,为java语言添加了很对新的特性,其中一个就是Optional,网上有很多文章介绍如何使用Optional优雅的解决NPE问题,但是“优雅”在哪呢?本篇文章就是介绍“优雅”在何处。
Optional用法
网络上有很多单独讲如何使用Optional的文章,我就不赘述了,可以找一下。
而且在下文中全片贯通如何使用,请继续阅读。
Optional“优雅”的核心
Optional优雅解决NPE问题的核心就是只关注最终使用的对象或属性。何为“最终的对象或属性”?Optional的一个使用场景就是连续的属性调用,如下代码:
people.getCar().getCenterConsole().getState();
这种连续的对象属性调用是最容易出现NPE问题的地方。而最后的**getState()**就是“最终的对象或属性”。
我们最终使用的就是state这个属性,所以不能因为前面的car或者centerConsole对象为null造成我们最终关心的对象state出现问题而无法使用。
具体讲解
使用orElse()方法解决NPE问题
public class OptionalTest {
public static void main(String[] args) {
People people = new People();
Car car = new Car();
CenterConsole centerConsole = new CenterConsole();
people.setCar(car);
car.setCenterConsole(centerConsole);
centerConsole.setState("关闭");
String centerState = Optional.ofNullable(people)
.map(s->s.getCar())
.map(s->s.getCenterConsole())
.map(s->s.getState())
.orElse("unknow");
System.out.println(centerState);
}
}
orElse()的作用就是当发生NPE是,返回指定的默认值,此处就是“unknow"。
在orElse()之前有三个map()方法,对应的就是如下代码片段:
people.getCar().getCenterConsole().getState();
在三个map()方法中即级联调用getter()方法的过程中,有任何一处返回了null的话,最终均返回orElse()指定的默认值。这就是其核心“只关注最终使用的对象或属性”。所以后面使用这个“最终的对象或属性”的时候,还是要判断是否为默认值,如果是指定的默认值说明之前某个地方存在问题,需要单独处理。
使用ifPresent()方法解决NPE问题
public class OptionalTest {
public static void main(String[] args) {
People people = new People();
Car car = new Car();
CenterConsole centerConsole = new CenterConsole();
people.setCar(car);
car.setCenterConsole(centerConsole);
centerConsole.setState("关闭");
Optional.ofNullable(people)
.map(s->s.getCar())
.map(s->s.getCenterConsole())
.ifPresent(s->s.setState("开启"));
System.out.println(centerConsole.getState());
}
}
这里只有两个map()方法的原因是java中对对象类型和基本数据类型的传递方式不同。
以上代码在car或者centerConsole为null时返回“关闭”,如果均不为空则返回“开启”。
关于性能
public class OptionalTest {
public static void main(String[] args) {
int total = 10000;//循环次数
People people = new People();
Car car = new Car();
CenterConsole centerConsole = new CenterConsole();
people.setCar(car);
car.setCenterConsole(centerConsole);
centerConsole.setState("关闭");
long startTime = System.currentTimeMillis();
for (int i = 0;i<total;i++) {
Optional.ofNullable(people).map(s -> s.getCar()).map(s -> s.getCenterConsole()).map(s->s.getState()).orElse("unknow");
}
long endTime = System.currentTimeMillis();
long startTime2 = System.currentTimeMillis();
for (int i = 0;i<total;i++) {
if (null != people){
Car car1 = people.getCar();
if (null != car1){
CenterConsole centerConsole1 = car1.getCenterConsole();
if (null != centerConsole1){
centerConsole.setState("开启");
}
}
}
}
long endTime2 = System.currentTimeMillis();
System.out.println((endTime-startTime)+" "+(endTime2-startTime2));
System.out.println(centerConsole.getState());
}
}
如上测试代码:
- 当总循环次数<10000时,使用Optional与普通的嵌套null非空检测查询稳定在30ms;
- 当总循环次数>10000&<10000000时,Optional比普通嵌套null非空检测查询性能查询10-20倍,Optional消耗时间稳定在35-65ms之间;
- 当总循环次数>10000000时,性能差距呈指数上升。