java动态添加枚举值,实现枚举值的动态扩展

直接上代码:

加入lombok依赖:

<dependencies>
        <dependency>
            <groupId>junitgroupId>
            <artifactId>junitartifactId>
            <version>4.13.1version>
        dependency>
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <version>1.18.24version>
        dependency>
        <dependency>
            <groupId>org.slf4jgroupId>
            <artifactId>slf4j-apiartifactId>
            <version>1.7.36version>
        dependency>
        <dependency>
            <groupId>ch.qos.logbackgroupId>
            <artifactId>logback-coreartifactId>
            <version>1.2.11version>
        dependency>
        <dependency>
            <groupId>ch.qos.logbackgroupId>
            <artifactId>logback-classicartifactId>
            <version>1.2.11version>
        dependency>
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>fastjsonartifactId>
            <version>1.2.83version>
        dependency>
    dependencies>

Enum工具类:


import lombok.extern.slf4j.Slf4j;
import sun.reflect.ConstructorAccessor;
import sun.reflect.FieldAccessor;
import sun.reflect.MethodAccessor;
import sun.reflect.ReflectionFactory;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Slf4j
public class EnumUtil {

    /**
     * 扩展枚举(把新枚举的值加入旧枚举里,从旧枚举里删除不要的枚举值)
     * @param oldEnumClass 旧枚举类
     * @param newEnumClass 需要扩展的枚举类
     * @param removeOldEnums 需要删除的旧枚举值
     * @param 
     * @param 
     * @throws Exception
     */
    public static <O extends Enum,N extends Enum> void extendEnum(Class<O> oldEnumClass,Class<N> newEnumClass,O... removeOldEnums) throws Exception {
        boolean needToRemoveOldEnum = removeOldEnums!=null&&removeOldEnums.length>0;
        if(needToRemoveOldEnum){
            removeEnum(oldEnumClass,removeOldEnums);
        }
        List newEnums = values(newEnumClass);
        Field[] fields = getFields(newEnumClass);
        for (Object newEnumObject : newEnums) {
            Enum newEnum = Enum.class.cast(newEnumObject);

            List<Class> fieldTypeList = new ArrayList<>();
            //枚举名称的类型:String.class
            fieldTypeList.add(String.class);
            for (Field field : fields) {
                fieldTypeList.add(field.getType());
            }
            List<Object> fieldValueList = new ArrayList<>();
            //枚举名称
            fieldValueList.add(newEnum.name());
            for (Field field : fields) {
                Object value = field.get(newEnum);
                fieldValueList.add(value);
            }

            Class[] fieldTypes = fieldTypeList.toArray(new Class[]{});
            String[] fieldValues = fieldValueList.toArray(new String[]{});
            addEnum(oldEnumClass,fieldTypes,fieldValues);
        }
    }

    /**
     * 新增枚举值
     * @param enumClass      枚举类型
     * @param fieldTypes 字段的类型,第一个是枚举名类型String
     * @param fieldValues 字段的值,第一个是枚举名称
     * @throws Exception
     */
    public static <T extends Enum<T>> T addEnum(Class<T> enumClass, Class[] fieldTypes, Object[] fieldValues) throws Exception {
        if(fieldTypes==null||fieldTypes.length==0){
            throw new RuntimeException("参数fieldTypes为空");
        }
        if(fieldValues==null||fieldValues.length==0){
            throw new RuntimeException("参数fieldValues为空");
        }
        if(fieldTypes[0]!=String.class){
            throw new RuntimeException("参数fieldTypes[0]不是String.class");
        }
        if(!(fieldValues[0] instanceof String)){
            throw new RuntimeException("参数fieldValues[0]不是字符串");
        }
        if(fieldTypes.length!=fieldValues.length){
            throw new RuntimeException("参数fieldTypes和参数fieldValues的长度不一致");
        }
        ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
        String enumName = fieldValues[0].toString();
        synchronized (enumClass){
            //判断name是否已经添加过了
            if(hasEnumName(enumClass,enumName)){
                log.info("枚举类{}中已存在该枚举名:{}",enumClass.getSimpleName(),enumName);
                return getEnum(enumClass,enumName);
            }
            //name,ordinal,其他自定义字段
            List<Class> allFieldClass = new ArrayList<>(fieldTypes.length + 1);
            allFieldClass.add(String.class);
            allFieldClass.add(int.class);
            for (int i = 1; i < fieldTypes.length; i++) {
                allFieldClass.add(fieldTypes[i]);
            }
            Class[] classes = allFieldClass.toArray(new Class[]{});
            Constructor<T> constructor = enumClass.getDeclaredConstructor(classes);
            ConstructorAccessor constructorAccessor = reflectionFactory.newConstructorAccessor(constructor);
            List<Object> allFields = new ArrayList<>(fieldValues.length + 1);
            allFields.add(enumName);
            int maxOrdinal = getMaxOrdinal(enumClass);
            allFields.add(maxOrdinal+1);
            for (int i = 1; i < fieldValues.length; i++) {
                allFields.add(fieldValues[i]);
            }
            //调用枚举的构造方法,创建新的枚举值
            T newEnum = (T) constructorAccessor.newInstance(allFields.toArray());
            log.info("新增枚举:{}" , newEnum);
            Field valuesField = enumClass.getDeclaredField("$VALUES");
            valuesField.setAccessible(true);

            //解除values属性的final限制
            Field modifiersField = Field.class.getDeclaredField("modifiers");
            modifiersField.setAccessible(true);
            int modifiers = modifiersField.getInt(valuesField);
            modifiers &= ~Modifier.FINAL;
            modifiersField.setInt(valuesField, modifiers);

            //将新增的枚举值加入values属性里
            FieldAccessor fieldAccessor = reflectionFactory.newFieldAccessor(valuesField, false);
            T[] ts = (T[]) fieldAccessor.get(enumClass);
            List<T> list = new ArrayList<>(Arrays.asList(ts));
            list.add(newEnum);
            fieldAccessor.set(enumClass, list.toArray(ts));

            //将Class对象的enumConstants和enumConstantDirectory清空(Enum.valueOf()方法会给它们赋值)
            /**
             * Enum.valueOf()逻辑:
             * 1.取enumConstantDirectory
             *      1.1如果有值则直接返回
             *      1.2如果没值,则取enumConstants,并拷贝到enumConstantDirectory,下次可直接返回
             *          1.2.1如果enumConstants有值,则返回
             *          1.2.2如果enumConstants没值,则取枚举的values属性,并拷贝到enumConstants,下次可直接返回
             * 2.enumConstantDirectory.get(name)返回
             */
            Field enumConstantDirectoryField = enumClass.getClass().getDeclaredField("enumConstantDirectory");
            enumConstantDirectoryField.setAccessible(true);
            FieldAccessor enumConstantDirectoryFieldAccessor = reflectionFactory.newFieldAccessor(enumConstantDirectoryField, false);
            enumConstantDirectoryFieldAccessor.set(enumClass,null);
            Field enumConstantsField = enumClass.getClass().getDeclaredField("enumConstants");
            enumConstantsField.setAccessible(true);
            FieldAccessor enumConstantsFieldAccessor = reflectionFactory.newFieldAccessor(enumConstantsField, false);
            enumConstantsFieldAccessor.set(enumClass,null);
            return newEnum;
        }
    }

    /**
     * 获取枚举类当前最大序号
     * @param enumClass 枚举类型
     * @param 
     * @return
     * @throws Exception
     */
    public static <T extends Enum<T>> int getMaxOrdinal(Class<T> enumClass) throws Exception {
        List<T> values = values(enumClass);
        if(values==null||values.size()==0){
            return 0;
        }
        int maxOrdinal = 0;
        for (T value : values) {
            if(maxOrdinal<value.ordinal()){
                maxOrdinal = value.ordinal();
            }
        }
        return maxOrdinal;
    }

    /**
     * 删除枚举值
     * @param enumClass 枚举类型
     * @param removeOldEnums 需要删除的枚举值
     * @param 
     */
    public static <T extends Enum<T>> void removeEnum(Class<T> enumClass, Enum<T>... removeOldEnums) throws Exception {
        if(removeOldEnums==null||removeOldEnums.length==0){
            log.warn("removeOldEnums为空");
            return;
        }
        ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
        synchronized (enumClass){
            Field valuesField = enumClass.getDeclaredField("$VALUES");
            valuesField.setAccessible(true);

            //解除values属性的final限制
            Field modifiersField = Field.class.getDeclaredField("modifiers");
            modifiersField.setAccessible(true);
            int modifiers = modifiersField.getInt(valuesField);
            modifiers &= ~Modifier.FINAL;
            modifiersField.setInt(valuesField, modifiers);

            FieldAccessor fieldAccessor = reflectionFactory.newFieldAccessor(valuesField, false);
            T[] oldEnumArray = (T[]) fieldAccessor.get(enumClass);
            List<T> enumList = new ArrayList<>(Arrays.asList(oldEnumArray));
            for (Enum removeOldEnum : removeOldEnums) {
                //将指定的枚举值从values属性里删除
                enumList.remove(removeOldEnum);
                log.info("删除枚举值:{}",removeOldEnum);
            }
            //把List转成数组
            T[] newEnumArray = (T[]) Arrays.copyOf(enumList.toArray(), enumList.size(), oldEnumArray.getClass());
            fieldAccessor.set(enumClass, newEnumArray);
        }
    }
	/**
     * 修改枚举属性
     * @param enumClass 枚举类型
     * @param enumName 枚举名称
     * @param attributeName 属性名
     * @param attributeValue 属性值
     * @param 
     * @throws Exception
     */
    public static <T extends Enum<T>> void setAttribute(Class<T> enumClass,String enumName,String attributeName,Object attributeValue) throws Exception {
        List<T> values = values(enumClass);
        T target = null;
        for (T t:values){
            if(t.name().equals(enumName)){
                target = t;
                break;
            }
        }
        if(target==null){
            throw new RuntimeException("该枚举类没有枚举名:"+enumName);
        }
        Field declaredField = target.getClass().getDeclaredField(attributeName);
        declaredField.setAccessible(true);
        declaredField.set(target,attributeValue);
    }

	/**
     * 修改枚举属性
     * @param targetEnum 枚举值
     * @param attributeName 属性名
     * @param attributeValue 属性值
     * @param 
     * @throws Exception
     */
    public static <T extends Enum<T>> void setAttribute(T targetEnum,String attributeName,Object attributeValue) throws Exception {
        Field declaredField = targetEnum.getClass().getDeclaredField(attributeName);
        declaredField.setAccessible(true);
        declaredField.set(targetEnum,attributeValue);
    }

    /**
     * 判断枚举类是否包含了指定枚举名
     * @param clazz
     * @param enumName
     * @param 
     * @return
     */
    public static <T extends Enum<T>> boolean hasEnumName(Class<T> clazz, String enumName) throws Exception {
//        不要用valueOf方法,因为它会初始化enumConstantDirectory使得后续调用valueOf方法后拿不到后面加入的枚举值
//        try{
//            T t = Enum.valueOf(clazz, enumName);
//            if(t!=null){
//                return true;
//            }
//        }catch (Exception e){
//            e.printStackTrace();
//            System.err.println(e.getMessage());
//        }
        List<T> values = values(clazz);
        for (T t:values){
            if(t.name().equals(enumName)){
                return true;
            }
        }
        return false;
    }

    /**
     * 根据枚举类型和枚举名获取枚举值
     * @param clazz 枚举类型
     * @param enumName 枚举名称
     * @param 
     * @return
     * @throws Exception
     */
    public static <T extends Enum<T>> T getEnum(Class<T> clazz, String enumName) throws Exception {
        List<T> values = values(clazz);
        for (T t:values){
            if(t.name().equals(enumName)){
                return t;
            }
        }
        return null;
    }

    /**
     * 获取枚举类的所有枚举值
     * @param clazz 枚举类型
     * @param 
     * @return
     * @throws Exception
     */
    public static <T extends Enum<T>> List<T> values(Class<T> clazz) throws Exception {
        ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
        Field valuesField = clazz.getDeclaredField("$VALUES");
        valuesField.setAccessible(true);
        FieldAccessor fieldAccessor = reflectionFactory.newFieldAccessor(valuesField, false);
        T[] ts = (T[]) fieldAccessor.get(clazz);
        List<T> list = new ArrayList<>(Arrays.asList(ts));
        return list;
    }

    /**
     * 获取枚举类的所有枚举值
     * @param clazz 枚举类型
     * @param 
     * @return
     * @throws Exception
     */
    public static <T extends Enum<T>> List<T> values2(Class<T> clazz) throws Exception {
        ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
        Method valuesMethod = clazz.getDeclaredMethod("values");
        valuesMethod.setAccessible(true);
        MethodAccessor methodAccessor = reflectionFactory.newMethodAccessor(valuesMethod);
        T[] ts = (T[]) methodAccessor.invoke(clazz, null);
        List<T> list = new ArrayList<>(Arrays.asList(ts));
        return list;
    }

    /**
     * 获取枚举的字段(排除数组类型、枚举类型)
     * @param enumClass
     * @return
     */
    public static Field[] getFields(Class<? extends Enum> enumClass){
        List<Field> result = new ArrayList<>();
        Field[] declaredFields = enumClass.getDeclaredFields();
        for (Field field : declaredFields) {
            Class<?> type = field.getType();
            //排除数组类型(values)、枚举类型(枚举值)
            if(type!=enumClass && !type.isArray()){
                field.setAccessible(true);
                result.add(field);
            }
        }
        return result.toArray(new Field[]{});
    }

    /**
     * 打印枚举值
     * @param enumClass 枚举类型
     * @throws Exception
     */
    public static void printEnum(Class<? extends Enum> enumClass) throws Exception {
        System.out.println("+++++++++++++++++++++++");
        List values = values(enumClass);
        values.stream().forEach(System.out::println);
        System.out.println("+++++++++++++++++++++++");
    }

}

有个第三方jar包的User类,User类里有个属性gender,类型是枚举GenderEnum:


import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;

@NoArgsConstructor
@SuperBuilder
@Data
public class User {
    private String username;
    private GenderEnum gender;
}

public enum GenderEnum {
    MAN("man","男"),
    WOMEN("women","女"),
    RENYAO("renyao","人妖"),
    ;
    private String key;
    private String displayName;
    GenderEnum(String key, String  displayName){
        this.key = key;
        this.displayName = displayName;
        System.out.println("调用了GenderEnum枚举构造器创建枚举值:"+name());
    }

    @Override
    public String toString() {
        return "GenderEnum{" +
                "ordinal="+ordinal()+
                ", name='"+name()+"'"+
                ", key='" + key + "'"+
                ", displayName='" + displayName + "'" +
                "}";
    }

}

现在项目需要使用User类,但是想去掉“人妖”这个枚举,改成“其他”(无法修改第三方jar包的源码)。
先定义新的枚举类EnhanceGenderEnum:


public enum EnhanceGenderEnum {
    MAN("man","男"),
    WOMEN("women","女"),
    OTHER("other","其他"),
            ;
    private String key;
    private String displayName;
    EnhanceGenderEnum(String key, String  displayName){
        this.key = key;
        this.displayName = displayName;
        System.out.println("调用了EnhanceGenderEnum枚举构造器创建枚举值:"+name());
    }

    @Override
    public String toString() {
        return "EnhanceGenderEnum{" +
                "ordinal="+ordinal()+
                ", name='"+name()+"'"+
                ", key='" + key + "'"+
                ", displayName='" + displayName + "'" +
                "}";
    }
}

如此一来,即可对GenderEnum进行增强了(见test()方法):


import com.alibaba.fastjson.JSON;
import org.junit.Assert;
import org.junit.Test;

import java.lang.reflect.Field;


public class GenderEnumTest {

    @Test
    public void printEnumTest() throws Exception{
        EnumUtil.printEnum(GenderEnum.class);
        EnumUtil.printEnum(EnhanceGenderEnum.class);
    }

    @Test
    public void addEnumTest() throws Exception{
        EnumUtil.printEnum(GenderEnum.class);
        GenderEnum addedEnum = EnumUtil.addEnum(GenderEnum.class, new Class[]{String.class,String.class,String.class}, new Object[]{"OTHER","other","其他"});
        EnumUtil.printEnum(GenderEnum.class);
        boolean addSuccess = false;
        for (GenderEnum value : GenderEnum.values()) {
            if(value==addedEnum){
                addSuccess = true;
            }
        }
        Assert.assertTrue(addSuccess);
        GenderEnum other = GenderEnum.valueOf("OTHER");
        Assert.assertTrue(other!=null&&other==addedEnum);
    }

    @Test
    public void removeTest() throws Exception{
        EnumUtil.printEnum(GenderEnum.class);
        EnumUtil.removeEnum(GenderEnum.class,GenderEnum.RENYAO);
        EnumUtil.printEnum(GenderEnum.class);
        boolean removeSuccess = true;
        for (GenderEnum value : GenderEnum.values()) {
            if(value==GenderEnum.RENYAO){
                removeSuccess = false;
            }
        }
        Assert.assertTrue(removeSuccess);
    }

    @Test
    public void hasEnumNameTest() throws Exception {
        boolean hasEnumName = EnumUtil.hasEnumName(GenderEnum.class, "RENYAO");
        Assert.assertTrue(hasEnumName);
    }

    @Test
    public void getEnumTest() throws Exception {
        GenderEnum anEnum = EnumUtil.getEnum(GenderEnum.class, "RENYAO");
        Assert.assertTrue(anEnum!=null);
    }

    @Test
    public void getFieldsTest(){
        Field[] fields = EnumUtil.getFields(GenderEnum.class);
        for (Field field : fields) {
            System.out.println(field);
        }
    }

    @Test
    public void extendEnumTest() throws Exception{
        EnumUtil.printEnum(GenderEnum.class);
        EnumUtil.extendEnum(GenderEnum.class,EnhanceGenderEnum.class,GenderEnum.RENYAO);
        EnumUtil.printEnum(GenderEnum.class);
    }
    
    @Test
    public void setAttributeTest1() throws Exception {
        System.out.println(GenderEnum.RENYAO);
        EnumUtil.setAttribute(GenderEnum.class,GenderEnum.RENYAO.name(),"displayName","其他");
        System.out.println(GenderEnum.RENYAO);
        EnumUtil.printEnum(GenderEnum.class);
    }
    
    @Test
    public void setAttributeTest2() throws Exception {
        System.out.println(GenderEnum.RENYAO);
        EnumUtil.setAttribute(GenderEnum.RENYAO,"displayName","其他");
        System.out.println(GenderEnum.RENYAO);
        EnumUtil.printEnum(GenderEnum.class);
    }

    @Test
    public void test() throws Exception{
        EnumUtil.extendEnum(GenderEnum.class,EnhanceGenderEnum.class,GenderEnum.RENYAO);
        User user = User.builder().username("sky").gender(GenderEnum.valueOf(EnhanceGenderEnum.OTHER.name())).build();
        String json = JSON.toJSONString(user);
        System.out.println(json);
        User user1 = JSON.parseObject(json, User.class);
        System.out.println(user1.getGender());
    }

}

你可能感兴趣的:(java,java,jvm,开发语言)