讀古今文學網 > 編寫高質量代碼:改善Java程序的151個建議 > 建議85:小心switch帶來的空值異常 >

建議85:小心switch帶來的空值異常

使用枚舉定義常量時,會伴有大量的switch語句判斷,目的是為每個枚舉項解釋其行為,例如這樣一個方法:


public static void doSports(Season season){

switch(season){

case Spring:

System.out.println("春天放風箏");

break;

case Summer:

System.out.println("夏天游泳");

break;

case Autumn:

System.out.println("秋天捉知了");

break;

case Winter:

System.out.println("冬天滑冰");

break;

default:

System.out.println("輸入錯誤!");

break;

}

}


上面的代碼中輸入了一個Season類型的枚舉,然後使用switch進行匹配,目的是輸出每個季節能進行的活動。現在的問題是:這段代碼有沒有問題?

我們先來看它是如何被調用的,因為要傳遞進來的是Season類型,也就是一個實例對象,那當然應該允許為空了,我們就傳遞一個null值進去看看有沒有問題,代碼如下:


public static void main(Stringargs){

doSports(null);

}


似乎會打印出「輸出錯誤!」,因為在switch中沒有匹配到指定值,所以會打印出default的代碼塊,是這樣的嗎?不是,運行後的輸出如下所示:


Exception in thread"main"java.lang.NullPointerException

at Client.doSports(Client.java:9)

at Client.main(Client.java:5)


竟然是空指針異常,第9行也就是switch那一行,怎麼會有空指針呢?這就與枚舉和switch的特性有關了,此問題也是在開發中經常發生的。我們知道,目前Java中的switch語句只能判斷byte、short、char、int類型(JDK 7已經允許使用String類型),這是Java編譯器的限制。問題是為什麼枚舉類型也可以跟在switch後面呢?

很簡單,因為編譯時,編譯器判斷出switch語句後的參數是枚舉類型,然後就會根據枚舉的排序值繼續匹配,也就是說上面的代碼與以下代碼相同:


public static void doSports(Season season){

switch(season.ordinal()){

case Season.Spring.ordinal():

……

case Season.Summer.ordinal():

……

}

}


看明白了吧,switch語句是先計算season變量的排序值,然後與枚舉常量的每個排序值進行對比的。在我們的例子中season變量是null值,無法執行ordinal方法,於是報空指針異常了。

問題清楚了,解決方法也很簡單,在doSports方法中判斷輸入參數是否是null即可。