Java程序运行中动态添加枚举

今天在工作中遇到这么一个场景:
调用一个service功能,需要传递枚举,如果传递其他的值还需要修改底层的代码。于是就想到用动态的方式添加枚举。参考了其他人写的例子,自己尝试了一下,说不定之后可以用到,所以在这里纪录一下。

枚举:
@Getter
public enum Test {

    PT("服务质量", "bandwidth", "bandwidth", "", "", 1d, false, "带宽", "sum", "服务质量"),

    
    private String quotaName;           
    private String mark;                
    private String value;              
    private String content;           
    private String dig;        
    private double ratio;              
    private boolean perSeconds;        
    private String allName;            
    private String opt;               
    private String title;            

    Test(String quotaName, String mark, String value, String content, String dig, double ratio, boolean perSeconds, String allName, String opt, String title) {
        this.quotaName = quotaName;
        this.mark = mark;
        this.value = value;
        this.content = content;
        this.dig = dig;
        this.ratio = ratio;
        this.perSeconds = perSeconds;
        this.allName = allName;
        this.opt = opt;
        this.title = title;
	}
}


Utils:
// 动态添加枚举对象
public class DynamicEnumUtils {
    private static ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();

private static void setFailsafeFieldValue(Field field, Object target, Object value) throws NoSuchFieldException,
        IllegalAccessException {

    // let's make the field accessible
    field.setAccessible(true);

    // next we change the modifier in the Field instance to
    // not be final anymore, thus tricking reflection into
    // letting us modify the static final field
    Field modifiersField = Field.class.getDeclaredField("modifiers");
    modifiersField.setAccessible(true);
    int modifiers = modifiersField.getInt(field);

    // blank out the final bit in the modifiers int
    modifiers &= ~Modifier.FINAL;
    modifiersField.setInt(field, modifiers);

    FieldAccessor fa = reflectionFactory.newFieldAccessor(field, false);
    fa.set(target, value);
}

private static void blankField(Class<?> enumClass, String fieldName) throws NoSuchFieldException,
        IllegalAccessException {
    for (Field field : Class.class.getDeclaredFields()) {
        if (field.getName().contains(fieldName)) {
            AccessibleObject.setAccessible(new Field[] { field }, true);
            setFailsafeFieldValue(field, enumClass, null);
            break;
        }
    }
}

private static void cleanEnumCache(Class<?> enumClass) throws NoSuchFieldException, IllegalAccessException {
    blankField(enumClass, "enumConstantDirectory"); // Sun (Oracle?!?) JDK 1.5/6
    blankField(enumClass, "enumConstants"); // IBM JDK
}

private static ConstructorAccessor getConstructorAccessor(Class<?> enumClass, Class<?>[] additionalParameterTypes)
        throws NoSuchMethodException {
    Class<?>[] parameterTypes = new Class[additionalParameterTypes.length + 2];
    parameterTypes[0] = String.class;
    parameterTypes[1] = int.class;
    System.arraycopy(additionalParameterTypes, 0, parameterTypes, 2, additionalParameterTypes.length);
    return reflectionFactory.newConstructorAccessor(enumClass.getDeclaredConstructor(parameterTypes));
}

//(enumType, enumName, values.size(), additionalTypes, additionalValues);
private static Object makeEnum(Class<?> enumClass, String value, int ordinal, Class<?>[] additionalTypes,
                               Object[] additionalValues) throws Exception {
    Object[] parms = new Object[additionalValues.length + 2];
    parms[0] = value;
    parms[1] = Integer.valueOf(ordinal);
    System.arraycopy(additionalValues, 0, parms, 2, additionalValues.length);
    return enumClass.cast(getConstructorAccessor(enumClass, additionalTypes).newInstance(parms));
}

/**
 * exist
 *
 * @param values
 * @param enumName
 * @param <T>
 * @return
 */
public static <T extends Enum<?>> boolean contains(List<T> values, String enumName) {
    for (T value : values) {
        if (value.name().equals(enumName)) {
            return true;
        }
    }
    return false;
}

/**
 * Add an enum instance to the enum class given as argument
 *
 * @param <T> the type of the enum (implicit)
 * @param enumType the class of the enum to be modified
 * @param enumName the name of the new enum instance to be added to the class.
 */
@SuppressWarnings("unchecked")
public static <T extends Enum<?>> void addEnum(Class<T> enumType, String enumName, Class<?>[] additionalTypes, Object[] additionalValues) {

    // 0. Sanity checks
    if (!Enum.class.isAssignableFrom(enumType)) {
        throw new RuntimeException("class " + enumType + " is not an instance of Enum");
    }

    // 1. Lookup "$VALUES" holder in enum class and get previous enum instances
    Field valuesField = null;
    Field[] fields = enumType.getDeclaredFields();
    for (Field field : fields) {
        if (field.getName().contains("$VALUES")) {
            valuesField = field;
            break;
        }
    }
    AccessibleObject.setAccessible(new Field[] { valuesField }, true);

    try {

        // 2. Copy it
        T[] previousValues = (T[]) valuesField.get(enumType);
        List<T> values = new ArrayList<T>(Arrays.asList(previousValues));

        // 3. build new enum
        T newValue = (T) makeEnum(enumType, enumName, values.size(), additionalTypes, additionalValues);

        if(contains(values, enumName)){
            System.out.println("已存在");
            return;
        }

        // 4. add new value
        values.add(newValue);

        // 5. Set new values field
        setFailsafeFieldValue(valuesField, null, values.toArray((T[]) Array.newInstance(enumType, 0)));

        // 6. Clean enum cache
        cleanEnumCache(enumType);

    } catch (Exception e) {
        throw new RuntimeException(e.getMessage(), e);
    }
}



    public static void main(String[] args) {

        DynamicEnumUtils.addEnum(T.class, "pppp", new Class[]{
                String.class, String.class, String.class, String.class, String.class,
                double.class, boolean.class, String.class, String.class, String.class
        }, new Object[]{
                "QPS", "pp", "pp", "pp", "pp",
                100d, false, "最大QPS", "avg", "最大QPS"
        }); 

        for(T temp : T.values()){
            System.out.println(temp.getAllName());
        }

	}
}

遇到了一个很奇怪的事情,就是在添加这个工具类之前,枚举的构造函数被定义成了private,运行报错。删除private关键字之后仍然报错,但是重新生成一波构造函数之后这个报错莫名奇妙的好了。-- 记录一下,等到时候搞清楚了再来填坑。

参考:
Java动态生成枚举类型

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值