Java中三元运算符与自动装箱的NPE问题?

21

今天早上,我遇到了一个非常奇怪的空指针异常(NPE),并将其简化为一个简单的例子。这是JVM的bug还是正确的行为?

public class Test1 {
    class Item {
        Integer id = null;
        public Integer getId() {return id;}
    }   
    public Integer f() {
        Item item = new Item();
        // this works:
        //return item == null ? new Integer(1) : item.getId();

        // NPE??
        return item == null ? 1 : item.getId();
    }   
    public static void main(String[] args) {
        Test1 t = new Test1();
        System.out.println("id is: " + String.valueOf(t.f()));
    }   
}

编译和运行的输出:

$ javac Test1.java 
$ java Test1
Exception in thread "main" java.lang.NullPointerException
at Test1.f(Test1.java:12)
at Test1.main(Test1.java:16)
$

1
请使用Integer.valueOf(1)代替new Integer(1) - ratchet freak
1
这是一个很好的自动装箱示例,这也是为什么流行的最佳实践建议使用基本类型而不是包装器的原因。 - Cygnusx1
1
非常类似于Java中的自动装箱NullPointerException问题。 - Pops
5个回答

34
表达式 item == null ? 1 : item.getId() 的类型是int而不是Integer。因此,Java需要自动将您的Integer转换为int(导致NullPointerException)。然后它会将结果自动装箱回一个Integer(如果没有NullPointerException的话它就会这样做)以从方法返回。
另一方面,表达式item == null ? new Integer(1) : item.getId()的类型为Integer,不需要进行自动拆箱操作。
当您尝试自动拆箱一个null Integer时,您会得到一个NullPointerException(请参见Autoboxing),这就是您遇到的情况。
回答您的问题,这是正确的行为。

3
如果你反编译该类文件,你会清楚地看到你的NPE...
return Integer.valueOf(item != null ? item.getId().intValue() : 1);

3

item 可能不是 null,但当您调用 getId() 时,它返回的是 null。 当您尝试自动取消装箱 null 时,会出现 NPE。


你确定吗?getId返回一个可能为空的整数,因此不应该有自动装箱。如果你所说的是真的,那么为什么返回new Integer(1)会使NPE消失呢? - Kevin
6
使用“new Integer(1)”作为条件表达式时,第二个和第三个操作数都是“Integer”类型,所以条件表达式的类型也是“Integer”。而使用“1”作为条件表达式时,条件表达式的类型是“int”,因此“getId()”方法的返回值将被拆箱为“int”。(请参考JLS的第15.25节了解相关规则。) - dlev

2
下面的返回类型是 Integer -
public Integer f() {
    Item item = new Item();
    // this works:
    //return item == null ? new Integer(1) : item.getId();

    // NPE??
    return item == null ? 1 : item.getId();
}

以下是相关结果 -
item == null ? 1 : item.getId()

在你的情况下,null是空值。

因此,JVM会抛出NPE,因为它试图自动装箱null

尝试 -

new Integer(null); // and
Integer.valueOf(null);

两者都会抛出NPE异常。


2

这是因为您正在使用条件运算符?。以下代码

return item == null ? 1 : item.getId();

相当于

int result = item == null ? 1 : item.getId();
return result;

你的表达式中第一个操作数是int类型,因此结果也是int类型。这就是当你使用Integer显式包装1时代码可以正常运行的原因。在这种情况下,编译器会创建类似以下的代码:

Integer result = item == null ? new Integer(1) : item.getId();
return result;

因此,当尝试将item.getId()(为null)转换为int时,会发生NPE。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接