2017-03-17 23 views
4

考慮下面的代碼:爲什麼空數組引用的數組訪問表達式不引發NullPointerException?

int[] r = null; 
r[0] = 1 % 0; 

我本來期望這扔NullPointerException:根據JLS Sec 15.7.1:二元運算

左手操作數出現之前的任何全面評估評估右側操作數的一部分。

=是一個二進制運算符(在JLS Sec 15.2所示 - JLS二段15.26描述賦值運算符),和完全評估左邊的操作數將導致NullPointerException。但是,會引發ArithmeticException,表示在完全評估左側操作數之前評估右側操作數。

爲什麼?

回答

4

the simple assignment operator的說明書描述了此行爲:

...

如果左邊的操作數是一個數組訪問表達式(§15.10.3),可能包含在一個或多個對圓括號,則:

  • 首先,計算左側操作數數組訪問表達式的數組引用子表達式。如果此評估突然完成,則由於相同的原因,分配表達式會突然完成; (左側操作數數組訪問表達式)的索引子表達式和右側操作數不計算,並且不發生任何分配。

這通常完成。

  • 否則,左邊的操作數數組訪問表達式的指數的子表達式進行求值。如果此評估突然完成,則由於相同的原因,賦值表達式會突然完成,並且右側的操作數不會被計算並且不會分配。

這通常完成。

  • 否則,將評估右側的操作數。如果此評估突然完成,則由於同樣的原因,分配表達式會突然完成,並且不會進行分配。

這突然完成,ArithmeticException

  • 否則,如果該數組引用子表達式的值爲空,則沒有發生分配和拋出NullPointerException。

這永遠不會被執行。

因此,看起來至少在第15.7.1節的引用中存在不一致或過於簡化。


有趣的是,相同的行爲未對該化合物賦值運算符,例如觀察到的

int[] arr = null; 
arr[0] += 1 % 0; 

確實產量NullPointerException

JLS Sec 15.26.2描述了這一點。這也許較少令人吃驚,但是,因爲:

形式E1 op= E2的化合物賦值表達式是等效於E1 = (T) ((E1) op (E2)),其中TE1類型,不同之處在於E1只計算一次。

換句話說,這個代碼是(大約)等效於:

arr[0] = arr[0] + 1 % 0; 

所以NullPointerException發生在評價右手操作數的簡單賦值的。