[改善Java代碼]使用valueOf前必須進行校驗


每個枚舉都是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 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM