2012-10-02 80 views
2

This throwrew me。NullPointerException在平等檢查後使用Long

如果您有一個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

但我心中的邏輯學家很不高興。 A || B應該等於B ||答: –

+0

相關:[爲什麼比較Integer和int可以拋出Java中的NullPointerException?](https://stackoverflow.com/q/3352791) – Dukeling

回答

4

這就是問題所在:

value == -1 || value == null 
由左到右,自 Long必須先拆箱

表達式求值,JVM將這種以:

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

而且value.longValue()拋出NullPointerExceptionvaluenull的說法。它從來沒有達到表達的第二部分。

它工作時的順序是不同的,但:

value == null || value == -1 

因爲如果valuenull,第二部分(即能引起NullPointerExceptionvaluenull)永遠不會由於布爾表達式short-circuit evaluation執行。

這是規範還是JDK中的錯誤?

當然,這不是一個錯誤。順便原始值包裝紙是裝箱是按規格(5.1.8. Unboxing Conversion):

  • 如果rLong類型的基準,然後解包轉換轉換rr.longValue()

施加開箱後,其餘的是標準的Java。

+0

'valueOf(long)'auto-boxes。我認爲你的意思是'value.longValue()'解開這個值。 –

+0

@PeterLawrey:當然,非常感謝你! –

2

這是規範還是JDK中的錯誤?

這是正常現象。如果你解引用null的引用,你應該得到一個NullPointerException。這意味着如果你要檢查null你必須在發生這種情況之前檢查它。之後檢查是毫無意義和困惑的。

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

相同

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

和表達式的第一部分拋出第二部分被運行之前的NPE。如果第一部分沒有失敗,則第二部分必須是假的。

2

它是規範的一部分,特別是5.6.2. Binary Numeric Promotion5.1.8. Unboxing Conversion。相關部分:

5.6.2。二元數值提升

當經營者申請的二進制數值提升到一對操作數,每個都必須表示的值可以轉換爲數字類型,適用下列規則,依次是:

  1. 如果任何操作數都是引用類型,它將經歷拆箱轉換(§5.1.8)。

[...]

二元數值提升是對某些運營商的操作數執行:

[...]

  • 數值相等運算符==和! =(§15.21.1)

And:

5.1.8。取消裝箱轉換

[...]

  • 如果r類型長的基準,然後解包轉換將R 成r.longValue()

[...]

  • 如果r爲空,解包轉換拋出一個NullPointerException

注意if (value == null || value == -1)不會拋出,因爲短路評價例外。由於value == nulltrue,所以表達式value == -1的第二部分從未被評估,因此在這種情況下value未拆箱。

0

你的問題是在你的兩個測試中檢查null/primitive值的順序。由於您傳遞null,因此:

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

不會取消裝箱,因爲第一次檢查爲真。測試從左到右進行。這

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

將嘗試拆箱(以比較爲-1),但是,不能因爲你一個拆箱值null。預期這種行爲(解除引發異常的空值)。