Java三元运算符抛出NullPointerException问题解析:为何基本类型int 1的自动装箱未生效?
嘿,这个问题确实挺坑的,我当初第一次碰到的时候也懵了好一会儿!咱们先拆解下这三行代码的差异,就能明白为啥第三行偏偏抛出NPE了——核心原因是Java三元运算符?:的类型兼容规则在搞鬼。
先搞懂三元运算符的类型规则
Java要求三元运算符的两个结果表达式必须是兼容类型,编译器会自动做类型转换:要么把基本类型装箱成包装类,要么把包装类拆箱成基本类型,具体转换方向取决于两边的类型优先级。
逐行分析你的代码
咱们逐个看这三行打印语句:
第一行(正常运行):
System.out.println(getBox() != null ? getBox().getHeight() : Integer.valueOf(1));左边
getBox().getHeight()返回的是Integer(这里是null),右边Integer.valueOf(1)也是Integer类型。整个表达式的结果类型就是Integer,打印时直接处理null即可,不需要拆箱操作,所以完全没问题。第二行(正常运行):
System.out.println(getBox() != null ? null : 1);左边是
null(属于Integer类型的字面量),右边是基本类型int的1。这时候编译器会把右边的1自动装箱成Integer,让两边类型统一为Integer。表达式结果是Integer,打印null也不会触发拆箱,所以正常运行。第三行(抛出NPE):
System.out.println(getBox() != null ? getBox().getHeight() : 1);这里就是坑点了!左边是
Integer(getHeight()返回null),右边是基本类型int的1。根据Java的类型转换优先级,编译器会选择把左边的Integer拆箱成int类型,来和右边的int兼容——也就是说,整个表达式的结果类型被推导为int!当
getBox() != null为true时,我们拿到的是getHeight()返回的null,这时候要把这个null的Integer拆箱成int,必然会抛出NullPointerException!
关于“自动装箱未生效”的疑问
不是自动装箱没生效,而是编译器选择了拆箱而非装箱的转换方向。因为当一边是包装类、一边是基本类型时,Java会优先将包装类拆箱为基本类型,让表达式结果为基本类型,而不是把基本类型装箱成包装类。如果想要让这里触发装箱,你可以把右边的1强制转换为Integer,比如:
System.out.println(getBox() != null ? getBox().getHeight() : (Integer)1);
这样两边都是Integer类型,表达式结果为Integer,就不会触发拆箱,也就不会抛NPE了。
内容的提问来源于stack exchange,提问作者rik

