枚举工具类-反射获取枚举类中变量的所有值

2 年前

开发背景:

在开发中需要用到枚举类中变量的所有值。如下例季节枚举类中需要获取数组["春", "夏", "秋", "冬"]。

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<String> 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 <E, T>
     * @return
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
    public static <E, T> List<T> getValues(Class<E> enumClass, Class<T> paramClass)
            throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        E[] values = values(enumClass);
        Field field = getField(enumClass, paramClass);
        //因为枚举中字段的可能设置为private,要通过Field.get()方法获取Field的值需要开启忽略获取访问符限制
		field.setAccessible(true);
        List<T> resultList = new ArrayList<>();
        for (E e : values) {
            T t = (T)field.get(e);
            resultList.add(t);
        return resultList;
     * 通过反射调用枚举类的values获取所有枚举类
     * @param enumClass
     * @param <E>
     * @return
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
    private static <E> E[] values(Class<E> 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 <E>
     * @param <T>
     * @return
    private static <E, T> Field getField(Class<E> enumClass, Class<T> paramClass) throws IllegalAccessException {
        //如果是包装类获取包装类的基本类型
        Class basicClass = getBasicClass(paramClass);
        //获取类型相同的Field(类型相同或与其基本类型相同)
        List<Field> 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);

注释:这里我单独说下上述的getBasicClass方法:

  • 通过Integer的源码public static final Class<Integer> TYPE = (Class<Integer>) Class.getPrimitiveClass("int");可以得知TYPE属性存放了其基本类型。
  • 首先尝试通过class的getField("TYPE")获取TYPE属性。
  • 如果获取TYPE属性成功,则将通过Field中的get(Object obj)获取TYPE属性中的值。
  • 通过Field源码中get方法的注释可以得知,获取静态的Filed传入的Object为null即可,(因为static属于类不属于对象,所以无需指明具体的对象)

测试一下:

public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        //原始方式获取
        List<String> seaconList = new ArrayList<>();
        for (Season value : Season.values()) {
            String season = value.getSeason();
            seaconList.add(season);
        System.out.println(seaconList);
        //反射工具类获取
        List<String> 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;
}

再次运行输出:

再次运行输出