这么简单的三目运算符,竟然这么多坑?(二)

简介: 最近在一个业务改造中,使用三目运算符重构了业务代码,没想到测试的时候竟然发生 NPE 的问题。

案例 3

我们再稍微改造一下案例 1 的例子,如下所示:

boolean flag = true; //设置成true,保证表达式 2 被执行
int simpleInt = 66;
Integer nullInteger = null;
Integer result = flag ? nullInteger : simpleInt;

案例 3 与案例 1 右边部分完全相同,只不过左边部分的类型不一样,一个为基本数据类型 int,一个为 Integer

按照案例 1 的分析,这个也会发生 NPE 问题,原因与案例 1 一样。

这个之所以拿出来,其实想说下,上述三目运算符的结果为 int 类型,而左边类型为 Integer,所以这里将会发生自动装箱操作,将 int类型转化为 Integer

上述代码等同为:

Integer result = Integer.valueOf(flag ? nullInteger.intValue() : simpleInt);

案例 4

最后一个案例,与上面案例都不一样,代码如下:

boolean flag = true; //设置成true,保证表达式 2 被执行
Integer nullInteger = null;
Long objLong = Long.valueOf(88l);
Object result = flag ? nullInteger : objLong;

运行上述代码,依然将会发生 NPE 的问题。

这个案例表达式 2 与表达式 3 类型不一样,一个为 Integer,一个为 Long,但是这两个类型都是 Number的子类。

面对上述情况,JLS 规定:

Otherwise, binary numeric promotion (§5.6.2[2]) is applied to the operand types, and the type of the conditional expression is the promoted type of the second and third operands.

Note that binary numeric promotion performs value set conversion (§5.1.13[3]) and may perform unboxing conversion (§5.1.8[4]).

大白话讲,当表达式 2 与表达式 3 类型不一致,但是都为数字类型时,低范围类型将会自动转为高范围数据类型,即向上转型。这个过程将会发生自动拆箱。

Java 中向上转型并不需要添加任何转化,但是向下转换必须强制添加类型转换。

上述代码转化比较麻烦,我们先从字节码上来看:79.jpg

第一步,将 nullInteger拆箱。

第二步,将上一步的值转为 long 类型,即 (long)nullInteger.intValue()

第三步,由于表达式 2 变成了基本数据类型,表达式 3 为包装类型,根据案例 1 讲到的规则,包装类型需要转为基本数据类型,所以表达式 3 发生了拆箱。

第四步,由于三目运算符最后的结果类型为基本数据类型:long,但是左边类型为 Object,这里就需要把 long 类型装箱转为包装类型。

所以最后代码等同于:

Object result = Long.valueOf(flag ? (long)nullInteger.intValue() : objLong.longValue());

总结

看完上述四个案例,想必大家应该会有种感受,没想到这么简单的三目运算符,既然暗藏这么多「杀机」。

不过大家也不用过度害怕,不使用三目运算符。只要我们在开发过程重点注意包装类型的自动拆箱问题就好了,另外也要注意三目运算符的计算结果再赋值的时候自动拆箱引发的 NPE 的问题。

最好大家在开发过程中,都遵守一定的规范,即保持表达式 2 与表达式 3 的类型一致,不让 Java 编译器有自动拆箱的机会。

建议大家没事经常看下阿里出品的『Java 开发手册』,在最新的「泰山版」就增加三目运算符的这一节规范。

ps:公号消息回复:『开发手册』,获取最新版的 Java 开发手册。

80.jpg

最后一定要做好的单元测试,不要惯性思维,觉得这么简单的一个东西,看起来根本不可能出错的。

参考资料

[1]

15.25 节: https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.25

[2]

§5.6.2: https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.6.2

[3]

§5.1.13: https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.13

[4]

§5.1.8: https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.8

[5]

《Java 开发手册》解读:三目运算符为何会导致 NPE?: https://developer.aliyun.com/article/758784

相关文章
|
8月前
|
存储 算法 编译器
【c 语言 】移位操作符详解
【c 语言 】移位操作符详解
383 0
|
5月前
正则表达式的限定符、或运算符、字符类、元字符、贪婪/懒惰匹配
本文介绍了正则表达式中的限定符、或运算符、字符类、元字符以及贪婪与懒惰匹配的概念和用法。
47 5
|
7月前
|
编译器 C语言
【C语言】:中移位操作符,位操作符详运算规则详解
【C语言】:中移位操作符,位操作符详运算规则详解
59 1
|
7月前
|
编译器 C语言
C语言学习记录——操作符详解知识点选记(算术操作符、单目操作符、移位操作符、关系操作符、逻辑操作符、条件操作符......)二
C语言学习记录——操作符详解知识点选记(算术操作符、单目操作符、移位操作符、关系操作符、逻辑操作符、条件操作符......)二
59 3
|
7月前
|
存储 编译器 C语言
C语言学习记录——操作符详解知识点选记(算术操作符、单目操作符、移位操作符、关系操作符、逻辑操作符、条件操作符......)一
C语言学习记录——操作符详解知识点选记(算术操作符、单目操作符、移位操作符、关系操作符、逻辑操作符、条件操作符......)一
48 1
|
8月前
|
Python Windows
python语法中错误的运算符或分隔符
【5月更文挑战第19天】python语法中错误的运算符或分隔符
78 1
|
机器学习/深度学习 存储 Linux
Linux基本操作符(2)
Linux基本操作符(2)
70 0
|
8月前
|
C语言
【C语言】位操作符与移位操作符练习
【C语言】位操作符与移位操作符练习
【C语言】位操作符与移位操作符练习
|
存储 编译器 C语言
【C语言初阶】带你轻松玩转所有常用操作符(1) ——算数操作符,移位操作符,位操作符
【C语言初阶】带你轻松玩转所有常用操作符(1) ——算数操作符,移位操作符,位操作符
98 0
|
程序员
三目运算符的使用
三目运算符的使用
126 1

热门文章

最新文章