在开发中需要用到枚举类中变量的所有值。如下例季节枚举类中需要获取数组["春", "夏", "秋", "冬"]。
public enum Season {
SPRING("春"),
SUMMER("夏"),
AUTUMN("秋"),
WINTER("冬");
String season;
Season(String season) {
this.season = season;
}
public String getSeason() {
return season;
}
}
最开始,我使用传统的方式获取,代码如下:
public class EnumUtilsDemo {
public static void main(String[] args) {
List seaconList = new ArrayList<>();
for (Season value : Season.values()) {
String season = value.getSeason();
seaconList.add(season);
}
System.out.println(seaconList);
}
}
由于最近在学习到了反射相关的知识,所以想编写利用反射编写一个通用的工具类。一来强化学习,一来以后可以通用工具类。
我的实现思路是这样的:通过分析上述代码发现,获取所有值需要知道所在的枚举类和要获取的字段。这里我发现枚举类中的Field类型只有(以Season为例)如下:
Arrays.stream(Season.class.getDeclaredFields()).forEach(System.out::println);
输出:
public static final model.Season model.Season.SPRING
public static final model.Season model.Season.SUMMER
public static final model.Season model.Season.AUTUMN
public static final model.Season model.Season.WINTER
java.lang.String model.Season.season
private static final model.Season[] model.Season.$VALUES
所以我通过传入枚举类的class与要获取值的类型的class来获取。以Season为例,传入Season.class,与字段season的String.class来获取。
具体实现如下:
public class EnumUtils {
/**
* 获取枚举类中变量的列表
* @param enumClass 枚举类的class
* @param paramClass 参数类型的class
* @param
* @return
* @throws NoSuchMethodException
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
public static List getValues(Class enumClass, Class paramClass)
throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
E[] values = values(enumClass);
Field field = getField(enumClass, paramClass);
//因为枚举中字段的可能设置为private,要通过Field.get()方法获取Field的值需要开启忽略获取访问符限制
field.setAccessible(true);
List resultList = new ArrayList<>();
for (E e : values) {
T t = (T)field.get(e);
resultList.add(t);
}
return resultList;
}
/**
* 通过反射调用枚举类的values获取所有枚举类
* @param enumClass
* @param
* @return
* @throws NoSuchMethodException
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
private static E[] values(Class enumClass)
throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Method valuesMethod = enumClass.getMethod("values");
Object valuesObj = valuesMethod.invoke(enumClass);
E[] values = (E[]) valuesObj;
return values;
}
/**
* 通过field的类型获取field
* 说明:
* 因为枚举类中只有Enum类型的Field,所有可以通过指定Field的类型来获取Field
* @param enumClass
* @param paramClass
* @param
* @param
* @return
*/
private static Field getField(Class enumClass, Class paramClass) throws IllegalAccessException {
//如果是包装类获取包装类的基本类型
Class basicClass = getBasicClass(paramClass);
//获取类型相同的Field(类型相同或与其基本类型相同)
List fieldList = Arrays.stream(enumClass.getDeclaredFields())
.filter(f -> f.getType() == paramClass || f.getType() == basicClass).collect(Collectors.toList());
if (fieldList.size() != 1) {
//抛出异常,只支持一个属性
throw new IllegalArgumentException(paramClass + "类型属性数量异常。");
}
return fieldList.get(0);
}
/**
* 获取class的基本类型的class
* 说明:
* 存在基本类型将返回其基本类型,否则返回null
* 如传入Integer.class将返回int.class,传入String.class返回null
* @param paramClass
* @return
* @throws IllegalAccessException
*/
private static Class getBasicClass(Class paramClass) throws IllegalAccessException {
Field typeField = null;
try {
//尝试获取包装类的TYPE
typeField = paramClass.getField("TYPE");
} catch (NoSuchFieldException e) {
return null;
}
//获取包装类TYPE成功,获取TYPE属性值(因为类型为static,所以传入null)
return (Class) typeField.get(null);
}
}
测试一下:
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
//原始方式获取
List seaconList = new ArrayList<>();
for (Season value : Season.values()) {
String season = value.getSeason();
seaconList.add(season);
}
System.out.println(seaconList);
//反射工具类获取
List values = getValues(Season.class, String.class);
System.out.println(values);
}
输出:
原始方式获取:[春, 夏, 秋, 冬]
反射工具类获取:[春, 夏, 秋, 冬]
这里需要说明,需要获取属性的类型只能存在一个,否则会无法正常获取,如下列枚举类Season新增字段String name,再次运行将导致程序异常。这种情况下只能用原始方式获取了。
public enum Season {
SPRING("春", "SPRING"),
SUMMER("夏", "SUMMER"),
AUTUMN("秋", "AUTUMN"),
WINTER("冬", "WINTER");
String season;
String name;
Season(String season, String name) {
this.season = season;
this.name = name;
}
public String getSeason() {
return season;
}
public String getName() {
return name;
}
}
再次运行输出:
原始方式获取:[春, 夏, 秋, 冬]
[Ljava.lang.reflect.Field;@4554617c
Exception in thread "main" java.lang.IllegalArgumentException: class java.lang.String类型属性数量异常。
at EnumUtils.getField(EnumUtils.java:89)
at EnumUtils.getValues(EnumUtils.java:45)
at EnumUtils.main(EnumUtils.java:27)
当然使用反射工具类也有个弊端,就是需要抛出比较多的异常,但主要还是学习吧。通过上述工具类的开发,涉及到不少知识点,在开发过程中对这些知识又进一步的加深了。
有没讲明白的地方可以留言说明,也请大佬名对程序不足之处指出说明。