每个枚举都是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 }