Java开发手册学习(3)为什么使用三目运算符时必需要注意类型对齐?

162 阅读4分钟

3216159d04cd440c956a2f828468ed34_3.png

在阿里巴巴的Java开发手册中,提到了在使用三目运算符时必须要注意类型对齐。这是因为在三目运算符中,表达式1和表达式2需要进行类型对齐,而类型对齐的不当可能会导致意想不到的结果或者编译错误。

文档说明

【强制】三目运算符condition? 表达式1 : 表达式2中,高度注意表达式1和2在类型对齐时,可能抛出因自动拆箱导致的NPE异常。 说明:以下两种场景会触发类型对齐的拆箱操作: 1) 表达式1或表达式2的值只要有一个是原始类型。 2) 表达式1或表达式2的值的类型不一致,会强制拆箱升级成表示范围更大的那个类型。 反例: Integer a = 1; Integer b = 2; Integer c = null; Boolean flag = false; // ab的结果是int类型,那么c会强制拆箱成int类型,抛出NPE异常 Integer result=(flag? ab : c);

案例分析

下面我将通过一个代码案例来说明这个问题。

boolean boolean1 = true;
boolean boolean2 = false;
Boolean boolean3 = null;
boolean boolean4 = boolean1 ? boolean3 : boolean2;
System.out.println(boolean4);

以上代码,在运行过程中,会抛出NullPointerException异常

Exception in thread "main" java.lang.NullPointerException

查看反编译代码

为了一探究竟,对以上代码进行反编译,使用jad 工具进行反编译后,得到以下代码

boolean boolean1 = true;
boolean boolean2 = false;
Boolean boolean3 = null;
boolean boolean4 = boolean1 ? boolean3.booleanValue() : boolean2;
System.out.println(boolean4);

可以看到,反编译后的代码,编译器帮我们做了一次自动拆箱,而就是因为这次自动拆箱,导致代码出现对于一个null 对象boolean3.booleanValue()的调用,导致了NullPointerException。

自动装箱与自动拆箱

基本数据类型的自动装箱(autoboxing)、拆箱(unboxing) 是自J2SE 5.0 开始提供的功能。

一般我们要创建一个类的对象的时候,一般使用:

Class a = new Class(parameters);

当我们创建一个Integer 对象时,却可以这样:

Integer i = 100;

实际上,执行上面那句代码的时候,系统为我们执行了:

Integer i = Integer.valueOf(100); // 自动装箱

当我们创建一个int 变量时,是这样创建的:

int j = i;// 自动拆箱

我们可以简单理解为,当我们自己写的代码符合装(拆)箱规范的时候,编译器就会自动帮我们拆(装)箱。

自动装箱都是通过包装类的valueOf() 方法来实现的. 自动拆箱都是通过包装类对象的xxxValue() 来实现的(如booleanValue()、longValue() 等)。

总结自动装箱与自动拆箱,自动装箱就是将基本数据类型自动转换成对应的包装类,自动拆箱就是将包装类自动转换成对应的基本数据类型。

原理分析

通过查看反编译之后的代码,我们准确的定位到了问题,分析之后我们可以得出这样的结论:NullPointerException的原因应该是三目运算符和自动拆箱导致了空指针异常。

根据规定,三目运算符的表达式1 、表达式2操作数的返回值类型应该是一样的,这样才能当把一个三目运算符的结果赋值给一个变量。 如:

Person i = a > b : i1 : i2; 

就要求i1 和i2 的类型都必须是Person才行。

因为Java 中存在一种特殊的情况,那就是基本数据类型和包装数据类型,可以通过自动拆装箱的方式互相转换

即可以定义

int i = new Integer(100)

也可以定义

Integer i= 100

那如果,三目运算符的表达式1 和表达式2 的操作数的类型,分别是基本数据类型和包装类型对象时,就需要有一方需要进行自动拆装箱。

结果就是:由于使用了三目运算符,并且表达式1 、表达式12操作数分别是基本类型和对象。所以对对象进行拆箱操作,由于该对象为null,所以在拆箱过程中调用null.booleanValue() 的时候就报了NullPointerException。

最后总结

总结来说,阿里巴巴的Java开发手册中提到的在三目运算符中,当表达式1和表达式2在类型对齐时,可能会抛出因自动拆箱导致的NullPointerException异常。为了避免这种异常的发生,我们可以使用显式的null检查来避免自动拆箱导致的NullPointerException异常。这样可以提高代码的健壮性和可靠性。