Java条件运算符的陷阱: 自动拆箱与NPE

Java中的条件运算符(又称三目运算符,形如 b ? x : y),相信很多人都不陌生。其只需一条语句即可完成 if else 代码块的功能,故日常开发中也是高频操作,但是稍有不慎、姿势不对,就会掉入自动拆箱的陷阱中发生NPE(Null Pointer Exception)

abstract

复现异常

我们先来做一个简单的测试,下述测试代码quickTest1() 预期结果应该是输出 “b : null”

1
2
3
4
5
public static void quickTest1() {
Integer a = null;
Integer b = true ? a : 12;
System.out.println("b : " + b);
}

执行结果,却恰恰出乎意料,发生了NPE

figure 1.png

分析异常

使用javap工具对上述测试用例生成的class字节码文件进行反编译

figure 2.png

从上图可以看出,a变量进行的自动拆箱操作,而由于其值为null,故调用 a.intValue() 出现了NPE,所以实际最终执行的代码如下所示:

1
Integer b = Integer.valueOf( true ? a.intValue() : 12 );

解决方案

自动装箱、拆箱是在JDK1.5后引入的新功能,在平时开发过程中,可以大大方便我们,但是其也有可能会成为Bug之源。在Java的条件运算符( b ? x : y)中,当x和y的类型不一致时,即发生自动拆箱,将x,y全部转换成基本类型。上述例子中,x表达式为变量a——Integer类型,而y表达式为12——int基本类型。类型不一致,所以即对变量a进行自动拆箱

对于如何避免发生NPE,这里给出两种解决方案:

  • 不使用条件运算符,改用传统的if-else语句块
  • 保证x、y表达式类型一致,避免发生自动拆箱

我们根据第二种方案,重新改写我们之前的测试代码,这里我们通过显式转换将x、y表达式均统一为Integer

1
2
3
4
5
public static void quickTest2() {
Integer a = null;
Integer b = true ? a : (Integer) 12;
System.out.println("b : " + b);
}

反编译该方法的class字节码文件,可以看到没有发生自动拆箱操作

figure 3.png

其方法执行结果,符合我们的预期且未发生NPE

figure 4.png

0%