java为枚举变量设置最后一个字段
我想更改在我的枚举类中使用反射声明的属性“name”的最终值,将其与枚举本身的名称一起使用,以便在自定义注释中使用它。但我面临着一种我无法调试的奇怪行为
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
@Service
@Slf4j
public class EnumNameEditor {
public static void throwErrorForNotDefinedErrorCodes() throws Exception {
Reflections reflections = new Reflections("com.");
Set<Class<? extends CustomClass>> classes = reflections.getSubTypesOf(CustomClass.class);
log.info("classes: {}",classes);
for (Class c : classes) {
if(c.isEnum()) {
changeNameInEnum(c);
}
}
}
private static<C extends Enum<C>> void changeNameInEnum(Class<C> c) throws Exception {
C[] codes = c.getEnumConstants();
String uniqueIdentifier = "uniqueIdentifier";
String name = "name";
log.info("c: {}",Arrays.asList(codes));
for (C e : codes) {
System.out.println("\n\n=============== changing: " + e.ordinal());
getValue(c,name,e);
setValue(c,e);
System.out.println("Ee : " + e);
getValue(c,name,e);
setFieldValue(c,e);
}
codes = c.getEnumConstants();
log.info("after c: {}",Arrays.asList(codes));
}
private static <C extends Enum<C>> void setFieldValue(Class<C> c, C e) throws Exception {
System.out.println("e: "+ e);
Field $VALUESField = c.getDeclaredField("$VALUES");
makeAccessible($VALUESField);
C[] oldValues = (C[]) $VALUESField.get(null);
oldValues[e.ordinal()] = e;
$VALUESField.set(null, oldValues);
$VALUESField = Class.class.getDeclaredField("enumConstants");
makeAccessible($VALUESField);
$VALUESField.set(c, oldValues);
try {
$VALUESField = Class.class.getDeclaredField("enumConstantDirectory");
makeAccessible($VALUESField);
Map<String,C> map = (Map<String, C>) $VALUESField.get(c);
System.out.println("map: " + map);
if(map != null) {
map.put(e.name(),e);
$VALUESField.set(c, map);
}
} catch (Exception exc) {
exc.printStackTrace();
log.debug("exception while setting new enum values in enumConstantDirectory for class: {}",c);
}
}
static<C extends Enum<C>> Object getValue(Class<C> c, String fname, C e) throws Exception {
Field field = c.getDeclaredField(fname);
makeAccessible(field);
Object value = field.get(e);
System.out.println("value defined: " + value + " for: " + fname);
return value;
}
static<C extends Enum<C>> C setValue(Class<C> c, C e) throws Exception {
Field field = c.getDeclaredField("name");
makeAccessible(field);
field.set(e,e.name());
return e;
}
static void makeAccessible(Field field) throws Exception {
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~ Modifier.FINAL);
}
public static void main(String[] args) {
try {
System.out.println("name: " + GenericResponseErrorCodes.UNEXPECTED_ERROR.name);
throwErrorForNotDefinedErrorCodes();
System.out.println("name: " + GenericResponseErrorCodes.UNEXPECTED_ERROR.name);
} catch (Exception e) {
e.printStackTrace();
}
}
}
我的枚举类如下
public enum GenericResponseErrorCodes implements CustomClass {
UNEXPECTED_ERROR(1,"Unexpected error has occurred. Please try again later."),
UNEXPECTED_ERROR2(2,"Unexpected error2 has occurred. Please try again later."),
UNEXPECTED_ERROR3(3,"Unexpected error3 has occurred. Please try again later.","def.prop"),
UNEXPECTED_ERROR4(4,"Unexpected error4 has occurred. Please try again later."),
UNEXPECTED_ERROR5(5,"Unexpected error5 has occurred. Please try again later.");
final String uniqueIdentifier = "G";
String key;
String message;
Integer code;
public final String name = "test_name";
GenericResponseErrorCodes( Integer code, String message) {
this.code = code;
this.message = message;
}
GenericResponseErrorCodes(Integer code, String message, String key) {
this.code = code;
this.key = key;
this.message = message;
}
}
当我执行主功能时,我会得到以下日志
name: test_name
c: [GenericResponseErrorCodes(uniqueIdentifier=G, key=null, message=Unexpected error has occurred. Please try again later., code=1, name=test_name), GenericResponseErrorCodes(uniqueIdentifier=G, key=null, message=Unexpected error2 has occurred. Please try again later., code=2, name=test_name), GenericResponseErrorCodes(uniqueIdentifier=G, key=def.prop, message=Unexpected error3 has occurred. Please try again later., code=3, name=test_name), GenericResponseErrorCodes(uniqueIdentifier=G, key=null, message=Unexpected error4 has occurred. Please try again later., code=4, name=test_name), GenericResponseErrorCodes(uniqueIdentifier=G, key=null, message=Unexpected error5 has occurred. Please try again later., code=5, name=test_name)]
=============== changing: 0
value defined: test_name for: name
Ee : GenericResponseErrorCodes(uniqueIdentifier=G, key=null, message=Unexpected error has occurred. Please try again later., code=1, name=test_name)
value defined: UNEXPECTED_ERROR for: name
=============== changing: 1
value defined: test_name for: name
Ee : GenericResponseErrorCodes(uniqueIdentifier=G, key=null, message=Unexpected error2 has occurred. Please try again later., code=2, name=test_name)
value defined: UNEXPECTED_ERROR2 for: name
=============== changing: 2
value defined: test_name for: name
Ee : GenericResponseErrorCodes(uniqueIdentifier=G, key=def.prop, message=Unexpected error3 has occurred. Please try again later., code=3, name=test_name)
value defined: UNEXPECTED_ERROR3 for: name
=============== changing: 3
value defined: test_name for: name
Ee : GenericResponseErrorCodes(uniqueIdentifier=G, key=null, message=Unexpected error4 has occurred. Please try again later., code=4, name=test_name)
value defined: UNEXPECTED_ERROR4 for: name
=============== changing: 4
value defined: test_name for: name
Ee : GenericResponseErrorCodes(uniqueIdentifier=G, key=null, message=Unexpected error5 has occurred. Please try again later., code=5, name=test_name)
value defined: UNEXPECTED_ERROR5 for: name
after c: [GenericResponseErrorCodes(uniqueIdentifier=G, key=null, message=Unexpected error has occurred. Please try again later., code=1, name=test_name), GenericResponseErrorCodes(uniqueIdentifier=G, key=null, message=Unexpected error2 has occurred. Please try again later., code=2, name=test_name), GenericResponseErrorCodes(uniqueIdentifier=G, key=def.prop, message=Unexpected error3 has occurred. Please try again later., code=3, name=test_name), GenericResponseErrorCodes(uniqueIdentifier=G, key=null, message=Unexpected error4 has occurred. Please try again later., code=4, name=test_name), GenericResponseErrorCodes(uniqueIdentifier=G, key=null, message=Unexpected error5 has occurred. Please try again later., code=5, name=test_name)]
name: test_name
Process finished with exit code 0
我无法确定为什么使用反射访问的名称与枚举对象中的名称不同?还有,我应该如何永久更改名称字段? 提前谢谢
PS:我从this博客上获取了参考资料
# 1 楼答案
final String name = "test_name";
声明一个编译时常量在编译时,对该变量的所有(非反射)引用都将被
"test_name"
替换。因此,更改字段对这些访问没有影响,尤其是已在编译时确定
除此之外,90%的代码已经过时。您似乎不熟悉引用类型的概念。修改对象时,其标识不会更改,因此无需将引用写入已包含对其引用的变量
存储在
$VALUES
中的数组仍然保存着对e.ordinal()
索引中对象的引用,因此oldValues[e.ordinal()] = e;
是过时的,但同样地,即使设置此数组元素有效果,$VALUES
字段仍然引用了相同的数组,因此将此字段设置为它已经包含的相同数组引用,在任何情况下都是过时的这同样适用于存储在
enumConstants
(初始化时)中的映射。它仍然包含对同一对象的引用,无论是否已修改。读取映射以放置它已经包含的引用,同样地,将enumConstants
设置为它已经引用的映射是过时的值得注意的是,这两个字段并不是JRE中保存有关枚举类型的缓存信息的唯一位置,但谢天谢地,如上所述,当您更改被引用对象的字段时,您不需要更新这些引用
您所需要做的就是更改要修改的字段的声明,使其不会形成编译时常量,例如
将赋值移动到构造函数的替代方法有
因此,它不是编译时常量,尽管我们知道
toString()
将简单地返回对字符串本身的引用或者
在这里,大括号定义了一个初始值设定项,它会自动复制到每个构造函数中
然后,从你的反射操作中移除任何过时的东西,它变成:
它将产生与代码相同的输出,但最后一个print语句除外,它现在产生
如你所愿
当然,你可以简单地使用
使
name
字段冗余地包含枚举常量的名称,而不进行任何反射操作