java 相等检查后使用 Long 的 NullPointerException
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12686718/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
NullPointerException using Long after equality check
提问by Rob Challen
This threw me.
这把我扔了。
If you have a Java Long variable and you check equality against a primitive value using the == operator the runtime type of the value is changed to a primitive long.
如果您有一个 Java Long 变量并且您使用 == 运算符检查原始值是否相等,则该值的运行时类型将更改为原始 long。
Subsequent checking the variable for a null value then throws an unexpected NullPointerException.
随后检查变量是否为空值,然后抛出意外的 NullPointerException。
So in the test class:
所以在测试类中:
public class LongDebug {
public static void main(String[] args) {
Long validValue = 1L;
Long invalidValue = -1L;
Long nullValue = null;
System.out.println("\nTesting the valid value:");
testExpectedBehaviour(validValue);
testUnExpectedBehaviour(validValue);
System.out.println("\nTesting the invalid value:");
testExpectedBehaviour(invalidValue);
testUnExpectedBehaviour(invalidValue);
System.out.println("\nTesting the null value:");
testExpectedBehaviour(nullValue);
testUnExpectedBehaviour(nullValue);
}
/**
* @param validValue
*/
private static void testExpectedBehaviour(Long value) {
if (value == null || value == -1) System.out.println("Expected: The value was null or invalid");
else System.out.println("Expected: The value was valid");
}
private static void testUnExpectedBehaviour(Long value) {
try {
if (value == -1 || value == null) System.out.println("Unexpected: The value was null or invalid");
else System.out.println("Unexpected: The value was valid");
} catch (NullPointerException e) {
System.out.println("Unexpected: The system threw an unexpected NullPointerException");
}
}
}
The result I get is:
我得到的结果是:
Testing the valid value:
Expected: The value was valid
Unexpected: The value was valid
Testing the invalid value:
Expected: The value was null or invalid
Unexpected: The value was null or invalid
Testing the null value:
Expected: The value was null or invalid
Unexpected: The system threw an unexpected NullPointerException
Is this on spec or a bug in the JDK?
这是规范上的还是 JDK 中的错误?
采纳答案by Tomasz Nurkiewicz
This is the problem:
这就是问题:
value == -1 || value == null
Expressions are evaluated from left to right and since Long
must be unboxed first, JVM translates this to:
表达式从左到右计算,因为Long
必须先拆箱,JVM 将其转换为:
value.longValue() == -1 || value == null
And value.longValue()
throws NullPointerException
when value
is null
argument. It never reaches the second part of the expression.
并value.longValue()
抛出NullPointerException
whenvalue
是null
论点。它永远不会到达表达式的第二部分。
It works when the order is different though:
但它在顺序不同时有效:
value == null || value == -1
because if the value
is null
, the second part (that can cause NullPointerException
when value
is null
) is never executed due to boolean expression short-circuit evaluation.
因为如果value
is null
,则第二部分(可能导致NullPointerException
when value
is null
)由于布尔表达式短路评估而永远不会执行。
Is this on spec or a bug in the JDK?
这是规范上的还是 JDK 中的错误?
Of course this is not a bug. The way primitive value wrappers are unboxed is on spec (5.1.8. Unboxing Conversion):
当然,这不是一个错误。原始值包装器的拆箱方式在规范(5.1.8. Unboxing Conversion)上:
- If
r
is a reference of typeLong
, then unboxing conversion convertsr
intor.longValue()
- 如果
r
是类型的引用Long
,则拆箱转换转换r
为r.longValue()
After unboxing is applied, the rest is standard Java.
应用拆箱后,剩下的就是标准Java。
回答by Peter Lawrey
Is this on spec or a bug in the JDK?
这是规范上的还是 JDK 中的错误?
This is normal. If you dereference a reference which is null
you should get a NullPointerException. This means if you are going to check for null
you have to check it before this happens. Checking it after is pointless and confusing.
这是正常的。如果你取消引用一个引用,null
你应该得到一个 NullPointerException。这意味着如果您要进行检查,则null
必须在发生这种情况之前进行检查。之后检查它毫无意义且令人困惑。
if (value == -1 || value == null)
is the same as
是相同的
if (value.longValue() == -1 || value == null)
and the first part of the expression throws an NPE before the second part is run. If the first part doesn't fail the second part must be false.
并且表达式的第一部分在第二部分运行之前抛出一个 NPE。如果第一部分没有失败,第二部分必须是假的。
回答by verdesmarald
It's part of the spec, specifically 5.6.2. Binary Numeric Promotionand 5.1.8. Unboxing Conversion. The relevant parts:
它是规范的一部分,特别是5.6.2。二进制数字提升和 5.1.8。拆箱转换。相关部分:
5.6.2. Binary Numeric Promotion
When an operator applies binary numeric promotion to a pair of operands, each of which must denote a value that is convertible to a numeric type, the following rules apply, in order:
- If any operand is of a reference type, it is subjected to unboxing conversion (§5.1.8).
[...]
Binary numeric promotion is performed on the operands of certain operators:
[...]
- The numerical equality operators == and != (§15.21.1)
5.6.2. 二进制数字提升
当运算符将二进制数字提升应用于一对操作数时,每个操作数必须表示一个可转换为数字类型的值,以下规则依次适用:
- 如果任何操作数是引用类型,则将对其进行拆箱转换(第 5.1.8 节)。
[...]
对某些运算符的操作数执行二进制数字提升:
[...]
- 数值相等运算符 == 和 !=(第 15.21.1 节)
And:
和:
5.1.8. Unboxing Conversion
[...]
- If r is a reference of type Long, then unboxing conversion converts r into r.longValue()
[...]
- If r is null, unboxing conversion throws a NullPointerException
5.1.8. 拆箱转换
[...]
- 如果 r 是 Long 类型的引用,则拆箱转换会将 r 转换为 r.longValue()
[...]
- 如果 r 为 null,拆箱转换会抛出 NullPointerException
Note that if (value == null || value == -1)
doesn't throw the exception because of short-circuit evaluation. Since value == null
is true
, the second part of the expression value == -1
is never evaluated, so value
is not unboxed in this case.
请注意,if (value == null || value == -1)
不会因为短路评估而引发异常。由于value == null
is true
,表达式的第二部分value == -1
永远不会被计算,所以value
在这种情况下不会被拆箱。
回答by Brian Agnew
Your issue is the order of the checks for null/primitive value in your two tests. Since you're passing null, this:
您的问题是在您的两个测试中检查空/原始值的顺序。由于您传递的是空值,因此:
if (value == null || value == -1)
won't unbox, since the first check is true. The tests are performed left-to-right. This
不会拆箱,因为第一次检查是真的。测试是从左到右进行的。这
if (value == -1 || value == null)
will try to unbox (to compare to -1), however, and fail since you're unboxing a null
value. That behaviour (unboxing a null value throwing an exception) is expected.
然而,将尝试拆箱(与 -1 进行比较),但由于您正在拆箱一个null
值而失败。该行为(取消装箱引发异常的空值)是预期的。