5

这把我扔了。

如果您有一个 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中的错误?

4

4 回答 4

4

这就是问题:

value == -1 || value == null

表达式是从左到右计算的,由于Long必须先拆箱,JVM 将其转换为:

value.longValue() == -1 || value == null

并在争论时value.longValue()抛出。它永远不会到达表达式的第二部分。NullPointerExceptionvaluenull

它在顺序不同时起作用:

value == null || value == -1

因为如果valueis ,由于布尔表达式短路评估null,第二部分(可能导致NullPointerExceptionwhen valueis )永远不会执行。null

这是规范还是JDK中的错误?

当然,这不是一个错误。原始值包装器的拆箱方式符合规范(5.1.8.拆箱转换):

  • 如果r是类型的引用Long,则拆箱转换转换rr.longValue()

应用拆箱后,剩下的就是标准 Java。

于 2012-10-02T08:20:11.587 回答
2

这是规范还是JDK中的错误?

这很正常。如果你取消引用一个引用,null你应该得到一个 NullPointerException。这意味着如果您要检查,null则必须在发生这种情况之前检查它。之后检查它是没有意义的和令人困惑的。

if (value == -1 || value == null)

是相同的

if (value.longValue() == -1 || value == null)

并且表达式的第一部分在第二部分运行之前抛出一个 NPE。如果第一部分没有失败,则第二部分必须为假。

于 2012-10-02T08:20:25.377 回答
2

它是规范的一部分,特别是5.6.2。二进制数字提升5.1.8。拆箱转换。相关部分:

5.6.2. 二进制数字提升

当运算符将二进制数值提升应用于一对操作数时,每个操作数都必须表示一个可转换为数值类型的值,以下规则按顺序适用:

  1. 如果任何操作数属于引用类型,则将对其进行拆箱转换(第 5.1.8 节)。

[...]

对某些运算符的操作数执行二进制数值提升:

[...]

  • 数值相等运算符 == 和 != (§15.21.1)

和:

5.1.8。拆箱转换

[...]

  • 如果 r 是 Long 类型的引用,则拆箱转换将 r 转换为 r.longValue()

[...]

  • 如果 r 为 null,则拆箱转换将引发 NullPointerException

请注意,if (value == null || value == -1)不会因为短路评估而引发异常。因为value == nullis true,表达式的第二部分value == -1永远不会被计算,所以value在这种情况下不会被拆箱。

于 2012-10-02T08:21:20.497 回答
0

您的问题是在您的两个测试中检查空值/原始值的顺序。由于您传递的是空值,因此:

if (value == null || value == -1) 

不会拆箱,因为第一次检查是真的。测试从左到右进行。这

if (value == -1 || value == null)

但是,将尝试拆箱(与 -1 比较),并且由于您正在拆箱一个null值而失败。这种行为(取消装箱抛出异常的空值)是预期的。

于 2012-10-02T08:21:39.840 回答