每個枚舉都是java.lang.Enum的子類,都可以訪問Enum類提供的方法,比如hashCode(),name(),valueOf()等.....
其中valueOf()方法會把一個String類型的名稱轉變為枚舉項,也就是枚舉項中查找出字面值與該參數相等的枚舉項,雖然這個方法很簡單,但是JDK卻做了一個對於開發人員來說並不簡單的處理:
看代碼:
1 import java.util.Arrays; 2 import java.util.List; 3 4 public class Client { 5 public static void main(String[] args) { 6 //注意summer是小寫 7 List<String> params = Arrays.asList("Spring", "summer"); 8 for (String name : params) { 9 //查找表面值與name相同的枚舉項 10 Season s = Season.valueOf(name); 11 if (s != null) { 12 // 有該枚舉項時的處理 13 System.out.println(s); 14 } else { 15 // 沒有該枚舉項時的邏輯處理 16 System.out.println("無相關枚舉項"); 17 } 18 } 19 20 } 21 22 } 23 24 enum Season { 25 Spring, Summer, Autumn, Winter; 26 }
運行輸出:
Spring Exception in thread "main" java.lang.IllegalArgumentException: No enum constant cn.summerchill.test.Season.summer at java.lang.Enum.valueOf(Unknown Source) at cn.summerchill.test.Season.valueOf(Client.java:1) at cn.summerchill.test.Client.main(Client.java:12)
這段代碼看起來很完美了,其中考慮到從String轉換成枚舉類型可能不成功的情況,比如沒有匹配到指定的值,此時valueof的返回值應該為空,所以后面又緊跟着if....else判斷輸出.
但是運行結果拋出異常.報告是無效參數異常...也就說summer(小寫s)午飯轉換為Season枚舉,無法轉換那也不應該拋出IllegalArgumentException異常啊,一旦拋出這個異常,后續的代碼就不能執行了,這才是要命的,
這與我們的習慣用法不一致,例如我們從List中查找一個元素,即使不存在也不會報錯,頂多indexOf方法返回-1.
看源碼:
1 public static <T extends Enum<T>> T valueOf(Class<T> enumType, 2 String name) { 3 T result = enumType.enumConstantDirectory().get(name);//通過反射,從常量列表中查找. 4 if (result != null) 5 return result; 6 if (name == null) 7 throw new NullPointerException("Name is null"); 8 throw new IllegalArgumentException(//最后報無效參數異常 9 "No enum constant " + enumType.getCanonicalName() + "." + name); 10 }
valueOf方法先通過反射從枚舉類的常量聲明中查找,若找到就直接返回,若找不到就拋出無效參數異常.
valueOf方法本意是保護編碼中的枚舉安全性,使其不產生空枚舉對象,簡化枚舉操作,但是又引入了一個我們無法避免的IllegalArgumentException異常.
可能有讀者會所此處valueOf()方法的源代碼不對,以上源代碼是要輸入兩個參數,而我們的Season.valueOf()值傳遞一個String類型的參數.
真的是這樣嗎?是的,因為valueOf(String name)方法是不可見的,是JVM內置的方法,我們只有通過閱讀公開的valueOf方法來了解其運行原理.
在Season枚舉類中引用valueOf方法有三個:
但是在Enum的源碼中只有一個valueOf()的方法: 其他兩個方法都是JVM的內置方法...
問題清楚了,我們有兩種方式可以解決處理此問題:
(1)使用try....catch捕獲異常
try { Season s = Season.valueOf(name); // 有該枚舉項時的處理 System.out.println(s); } catch (Exception e) { System.out.println("無相關枚舉項"); }
(2)擴展枚舉類:
由於Enum類定義的方法基本上都是final類型的,所以不希望被覆寫,那我們可以學習List和String,通過增加一個contains方法來判斷是否包含指定的枚舉項,然后再繼續轉換,看代碼:
1 enum Season { 2 Spring, Summer, Autumn, Winter; 3 public boolean contains(String _name){ 4 Season[] season = values(); 5 for(Season s:season){ 6 if(s.name().equals(_name)){ 7 return true; 8 } 9 } 10 return false; 11 12 } 13 }
Season枚舉具備了靜態方法contains()之后,就可以在valueOf前判斷一下是否包含指定的枚舉名稱了,若包含則可以通過valueOf轉換為Season枚舉,若不包含則不轉換.
總結代碼:
1 import java.util.Arrays; 2 import java.util.List; 3 4 public class Client { 5 public static void main(String[] args) { 6 // 注意summer是小寫 7 List<String> params = Arrays.asList("Spring", "summer"); 8 for (String name : params) { 9 // 查找表面值與name相同的枚舉項 10 // Season s = Season.valueOf(name); 11 // if (s != null) { 12 // // 有該枚舉項時的處理 13 // System.out.println(s); 14 // } else { 15 // // 沒有該枚舉項時的邏輯處理 16 // System.out.println("無相關枚舉項"); 17 // } 18 if (Season.contains(name)) { 19 Season s = Season.valueOf(name); 20 // 有該枚舉項時的處理 21 System.out.println(s); 22 } else { 23 24 System.out.println("無相關枚舉項"); 25 26 } 27 28 } 29 30 } 31 32 } 33 34 enum Season { 35 Spring, Summer, Autumn, Winter; 36 37 // 是否包含指定名稱的枚舉項 38 public static boolean contains(String name) { 39 // 所有的枚舉值 40 Season[] season = values(); 41 42 // 遍歷查找 43 for (Season s : season) { 44 if (s.name().equals(name)) { 45 return true; 46 } 47 } 48 return false; 49 } 50 }