为什么这个任务会导致NPE?

时间:2014-04-10 15:32:39

标签: java nullpointerexception ternary-operator

public class Npe {
    static class Thing {
        long value;
    }

    public static Map<Thing, Long> map;

    public static void main(String[] args) {
        Thing thing = new Thing();
        method(null); // returns -1
        method(thing); // returns 0
        map = new HashMap<Thing, Long>();
        method(null); // returns -1
        method(thing); // NullPointerException thrown inside this method call
    }

    public static long method(Thing thing) {
        if (thing == null) {
            return -1;
        }
        Long v = (map == null) ? thing.value : map.get(thing); // NPE here
        if (v == null) {
            v = thing.value;
        }
        return v;
    }
}

在第4次致电method()时,我会在NullPointerException内的指定行上抛出method()。如果我从

重构那一行
Long v = (map == null) ? thing.value : map.get(thing);

Long v;
if (map == null) {
    v = thing.value;
} else {
    v = map.get(thing);
}

我没有NullPointerException,方法的行为应该如此。问题是:为什么??

在我看来,编译器希望?运算符的结果为long,以便它自动取消装箱(从Long降级到long)调用map.get(thing)的结果(可能会返回null,因此会抛出NullPointerException)。恕我直言,它应该期望?运算符的结果为Long并自动装箱(将long提升为Longthing.value

更好的是,如果我重构这句话:

Long v = (map == null) ? thing.value : map.get(thing);

到此(明确地将long投射到Long):

Long v = (map == null) ? (Long)thing.value : map.get(thing);

我的IDE(IntelliJ)说演员阵容是多余的,但编译后的代码按预期工作,不会抛出NullPointerException :-D

2 个答案:

答案 0 :(得分:29)

考虑你的条件表达式:

(map == null) ? thing.value : map.get(thing)

该表达式的结果为long,因为thing.value的类型为long。见JLS §15.25 - Conditional Operator。 JLS 8中的表格是一个很好的补充。它阐明了不同输入类型的所有可能输出类型。与条件表达式类型相关的混淆程度如此之多。

现在,当您调用此方法时:

method(thing);

map不是null,因此表达式中的条件map == null的计算结果为false,然后评估map.get(thing)以获得结果。

由于map尚无条目,map.get(thing)将返回null。但由于结果类型为long,因此会对null执行取消装箱操作,从而产生NPE


现在,当您明确地将thing.value投射到Long时,表达式的类型变为Long。因此,对map.get(thing)的结果不执行取消装箱,null已分配给Long v

答案 1 :(得分:0)

这是我对正在发生的事情的理解:

使用Long v = (map == null) ? thing.value : map.get(thing); // NPE here

map.get(thing)返回Long null,然后尝试将其值展开到long(因为表达式类型,如果长) - 这会导致NPE。

但是,当您使用长格式时,您要小心避免对null Long进行拆箱操作。