[英]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
以便自动取消装箱(从Long
到long
降级)调用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): 对此(明确地将
long
为Long
):
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.value
为Long
,表达式的类型变为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)
返回一个为null
的Long
,然后尝试将其值解除为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.