简体   繁体   English

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

[英]Why does this assignment cause NPE?

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;
    }
}

On the 4th call to method() I get a NullPointerException thrown on the indicated line inside method() . 在第4次调用method()我在method()内的指示行上抛出了NullPointerException If I refactor that line from 如果我重构那条线

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

to

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

I get no NullPointerException and the method behaves as it should. 我没有得到NullPointerException并且该方法的行为应该如此。 The question is: WHY?? 问题是:为什么?

It looks to me like the compiler expects the result of the ? 它看起来像编译器期望的结果? operator to be long so that it is automatically unboxing (demoting from Long to long ) the result of the call to map.get(thing) (which may return null and therefore throw a NullPointerException ). 运算符很long以便自动取消装箱(从Longlong降级)调用map.get(thing) (可能返回null并因此抛出NullPointerException )。 IMHO it should be expecting the result of the ? 恕我直言,它应该期待的结果? operator to be Long and autoboxing (promoting long to Long ) thing.value instead. 运算符为Long和autoboxing(提升long to Long )的thing.value

Even better, if I refactor this statement: 更好的是,如果我重构这个陈述:

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

to this (casting long to Long explicitly): 对此(明确地将longLong ):

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

my IDE (IntelliJ) says that the cast is redundant, but the compiled code works as expected and does not throw a NullPointerException ! 我的IDE(IntelliJ)说转换是多余的,但编译的代码按预期工作,不会抛出NullPointerException :-D :-D

Consider your conditional expression: 考虑你的条件表达式:

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

The result of that expression will be long , because the type of thing.value is long . 该表达式的结果很long ,因为thing.value的类型很long See JLS §15.25 - Conditional Operator . 参见JLS§15.25 - 条件运算符 The table in JLS 8 is a great addition. JLS 8中的表格是一个很好的补充。 It clarifies all the possible output types for different input types. 它阐明了不同输入类型的所有可能输出类型。 So much was the confusion related to the type of conditional expressions. 与条件表达式类型相关的混淆程度如此之多。

Now when you invoke this method as: 现在,当您调用此方法时:

method(thing);

The map is not null , so the condition map == null in your expression evaluates to false , and then it evaluates map.get(thing) to get the result. map不为null ,因此表达式中的条件map == null计算结果为false ,然后计算map.get(thing)以获取结果。

Since there is no entry in map yet, map.get(thing) will return null . 由于map没有条目, map.get(thing)将返回null But since the type of result is long , an unboxing operation is performed on null , which results in NPE . 但由于结果类型很long ,因此对null执行拆箱操作,这会导致NPE


Now when you explicitly cast thing.value to Long , the type of expression becomes Long . 现在,当您将thing.value显式thing.valueLong ,表达式的类型变为Long So no unboxing is performed on the result of map.get(thing) , and null is assigned to Long v . 因此,不对map.get(thing)的结果执行取消装箱,并将null分配给Long v

This is my understanding of what is happening : 这是我对正在发生的事情的理解:

when you use Long v = (map == null) ? thing.value : map.get(thing); // NPE here 当你使用Long v = (map == null) ? thing.value : map.get(thing); // NPE here Long v = (map == null) ? thing.value : map.get(thing); // NPE here

the map.get(thing) returns a Long that is null and then there is an attempt to unbox its value to long ( because the expression type if long ) - this causes NPE. map.get(thing)返回一个为nullLong ,然后尝试将其值解除为long (因为表达式类型为long) - 这会导致NPE。

However when you use the long form , you are carefully avoiding the unboxing operation on the null Long. 但是,当您使用长格式时,您要小心避免在null Long上进行取消装箱操作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM