这让我失望。
如果您有一个Java Long变量,并且使用==运算符检查与原始值的相等性,则值的运行时类型将更改为原始long。
随后检查变量的空值,然后抛出意外的NullPointerException。
所以在测试类中:
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");
}
}
}
我得到的结果是:
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
这是规格还是JDK中的错误?
答案 0 :(得分:4)
这是问题所在:
value == -1 || value == null
表达式从左到右进行评估,由于Long
必须首先取消装箱,因此JVM会将其转换为:
value.longValue() == -1 || value == null
当NullPointerException
为value
参数时,value.longValue()
会引发null
。它永远不会达到表达式的第二部分。
虽然订单不同,但它有效:
value == null || value == -1
因为如果value
是null
,则第二部分(NullPointerException
value
时可能导致null
)因布尔表达式而永远不会被执行short-circuit evaluation
这是规格还是JDK中的错误?
当然这不是错误。原始值包装器未装箱的方式符合规范(5.1.8. Unboxing Conversion):
- 如果
r
是Long
类型的引用,则取消装箱转化会将r
转换为r.longValue()
应用拆箱后,其余为标准Java。
答案 1 :(得分:2)
这是规格还是JDK中的错误?
这很正常。如果取消引用null
的引用,则应该获得NullPointerException。这意味着如果您要检查null
,则必须在此之前进行检查。检查之后是毫无意义和令人困惑的。
if (value == -1 || value == null)
与
相同if (value.longValue() == -1 || value == null)
并且表达式的第一部分在第二部分运行之前抛出NPE。如果第一部分没有失败,则第二部分必须是假的。
答案 2 :(得分:2)
这是规范的一部分,特别是5.6.2. Binary Numeric Promotion和 5.1.8. Unboxing Conversion。相关部分:
5.6.2。二进制数字促销
当运算符将二进制数字提升应用于一对操作数时,每个操作数必须表示可转换为数字类型的值,以下规则适用:
- 如果任何操作数属于引用类型,则进行拆箱转换(第5.1.8节)。
醇>[...]
对某些运算符的操作数执行二进制数字提升:
[...]
- 数字相等运算符==和!=(§15.21.1)
和
5.1.8。拆箱转换
[...]
- 如果r是Long类型的引用,则取消装箱转换会转换r 进入r.longValue()
[...]
- 如果r为null,则取消装箱转换会抛出NullPointerException
请注意,由于短路评估,if (value == null || value == -1)
不会引发异常。由于value == null
为true
,因此永远不会评估表达式value == -1
的第二部分,因此在这种情况下value
不会取消装箱。
答案 3 :(得分:0)
您的问题是两次测试中检查null / primitive值的顺序。由于您传递的是null,所以:
if (value == null || value == -1)
不会取消装箱,因为第一次检查是真的。测试从左到右进行。此
if (value == -1 || value == null)
然而,将尝试取消装箱(以比较为-1),并且因为您要取消装箱null
值而失败。该行为(取消装箱空值抛出异常)是预期的。