Java autoboxing和三元运算符疯狂

时间:2014-08-21 02:36:45

标签: java operator-keyword autoboxing ternary

花了几个小时来调试这段代码:

    LinkedHashMap<String, Integer> rsrqs = new LinkedHashMap<String, Integer>();
    Integer boxedPci = 52;
    Integer boxedRsrq = boxedPci != null ? rsrqs.get(boxedPci.toString()) : -1;

以上产生NullPointerException。以下代码不是:

    LinkedHashMap<String, Integer> rsrqs = new LinkedHashMap<String, Integer>();
    Integer boxedPci = 52;
    Integer boxedRsrq = boxedPci != null ? rsrqs.get(boxedPci.toString()) : Integer.valueOf(-1);

唯一的区别是用Integer.valueOf()包装-1。我确定一旦有人解释了为什么这段代码的行为方式,我就会打破我的额头..但是有人可以向我解释为什么这段代码的行为方式如下:)?

- 修改

第二个想法,我怀疑NPE来自rsrqs.get()返回null,我认为java在尝试重新装入int之前,然后装回一个Integer。 Integer.valueOf()强制Java执行unbox-box步骤。故事的道德启示;不要忽略Eclipse中的那些拳击警告;)

4 个答案:

答案 0 :(得分:27)

三元表达式与任何表达式一样,具有由编译器确定的类型。如果三元表达式的两边看起来像不同的类型,那么编译器将尝试使用两个选项中最不模糊的类型来查找公共基类型。在您的情况下,-1最不明确,因此三元表达式的类型为int。遗憾的是,编译器并没有根据接收变量使用类型推断。

然后评估表达式rsrqs.get(boxedPci.toString())并强制进入类型int以匹配三元表达式,但因为它null它会抛出NPE。

通过装箱-1,三元表达式的值为Integer,因此您可以安全无效。

答案 1 :(得分:11)

可以从java language specification: 15.25. Conditional Operator ? :中的信息中得出解释。

从表中可以获得信息,如果第二个操作数(rsrqs.get(boxedPci.toString()))的类型为Integer且第三个操作数的类型为int,则结果为属于int类型。

然而,这意味着

Integer boxedRsrq = boxedPci != null ? rsrqs.get(boxedPci.toString()) : -1;

在语义上与

相同
Integer boxedRsrq = boxedPci != null ? ((int)rsrqs.get(boxedPci.toString())) : -1;

但这意味着,如果您从地图中获得NullPointerException,则会获得null,这显然会发生。

如果将第三个操作数转换为Integer,则第二个操作数将永远不会转换为int,并且不会发生NPE。

答案 2 :(得分:3)

1

是int,而不是Integer。因此,Java会将您的Integer解包为int,这会导致NullPointerException。当您自动取消装入null整数时,会导致NullPointerException。 (reference here

但是当你使用

 Integer.valueOf(-1) 

它不需要自动取消装箱,这不会导致例外。

答案 3 :(得分:3)

好吧,Integer.valueOf(String)返回Integer,-1是原始int。第一个示例被强制取消装箱,因为一个术语是原始的。您也可以使用

Integer boxedRsrq = boxedPci != null ? 
    rsrqs.get(boxedPci.toString()) : (Integer) -1;

将盒装-1。