gpt4 book ai didi

Java反射-枚举生成

转载 作者:行者123 更新时间:2023-11-30 08:07:56 25 4
gpt4 key购买 nike

我制作的程序可以使用反射生成类、枚举和接口(interface)的源代码,但我在生成枚举时遇到问题。

我的 EumTest 类(class)

public enum EnumTest{
a,b;

private String r;

private EnumTest(){

}

private void some(){

}

public int[] some2(int[] b){
return b;
}
}

生成枚举文件的方法

private void generateEnum(Class<?> cls,PrintWriter writer) {
this.writePackage(cls, writer);
this.writeAnnotations(writer, cls.getDeclaredAnnotations());
writer.write(Modifier.toString(cls.getModifiers())+ " enum " + cls.getSimpleName());
this.writeImplementation(cls, writer);
writer.write("{");
this.writeNewLine(writer);
Object[] cons = cls.getEnumConstants();
for (int i = 0; i < cons.length; i++) {
writer.write(cons[i].toString());
if(i != cons.length - 1)
writer.write(",");
}
writer.write(";");
this.writeNewLine(writer);
this.writeFields(cls, writer);
this.writeConstructors(cls, writer);
this.writeMethods(cls,writer);
writer.write("}");
}

结果是这个新的枚举

package metaprogramovanie.test;
public final enum EnumTest{
a,b;
private java.lang.String r;
private static final metaprogramovanie.test.EnumTest[] $VALUES;

private EnumTest(java.lang.String arg0,int arg1){
}
public static metaprogramovanie.test.EnumTest[] values(){
return null;
}
public static metaprogramovanie.test.EnumTest valueOf(java.lang.String arg0){
return null;
}
private void some(){

}
public int daco(int arg0){
return 0;
}

}

正如您所看到的,存在一些错误。例如修饰符生成FINAL并且枚举不能是最终的,接下来有更多的方法和字段,构造函数有参数......

构造函数生成

private void writeConstructors(Class<?> cls, PrintWriter writer){
Constructor[] cons = cls.getDeclaredConstructors();
for (Constructor constructor : cons) {
this.writeAnnotations(writer, constructor.getDeclaredAnnotations());
writer.write(Modifier.toString(constructor.getModifiers()) + " " + cls.getSimpleName());
this.writeParameters(writer,constructor.getParameters());
writer.write("{");
this.writeNewLine(writer);
writer.write("}");
this.writeNewLine(writer);
}
}

字段生成

private void writeFields(Class<?> cls, PrintWriter writer){
Field[] atr = cls.getDeclaredFields();
for (Field field : atr) {
if(field.isEnumConstant()){
System.out.println("JE");
}
else{
this.writeAnnotations(writer, field.getDeclaredAnnotations());
writer.write(Modifier.toString(field.getModifiers())+" " + field.getType().getTypeName()+ " " + field.getName());
if(Modifier.isStatic(field.getModifiers())){
try{
Object value = field.get(null);
writer.write(" = " + this.getFieldValue(field));
}catch(IllegalAccessException ex){

}
}
writer.write(";");
this.writeNewLine(writer);
}
}
this.writeNewLine(writer);
}

方法生成

 private void writeMethods(Class<?> cls, PrintWriter writer){
Method[] methods = cls.getDeclaredMethods();
for (Method met : methods) {
this.writeAnnotations(writer, met.getDeclaredAnnotations());
writer.write(Modifier.toString(met.getModifiers())+" "+met.getReturnType().getTypeName() +" "+ met.getName());
this.writeParameters(writer, met.getParameters());
writer.write("{");
this.writeNewLine(writer);
if(!met.getReturnType().equals(Void.TYPE)){
this.writeMethodBody(writer,met);
}
this.writeNewLine(writer);
writer.write("}");
this.writeNewLine(writer);
}
this.writeNewLine(writer);
}

如果您有任何想法如何获取可编译的枚举。

最佳答案

这里解释了为什么你会得到所有额外的东西。

枚举是使用编译器合成的大量代码来实现的,因此它们在“幕后”按照 Java 语言规范指定的方式运行,而不需要对 JVM 进行大幅更改。

Java Language Specification例如,说:

  • enum 隐式是 final,除非存在带主体的常量。
  • 任何 enum 都会隐式声明 values()valueOf(String) 方法。

因此,您获得了 EnumTestfinal 修饰符,并且获得了两个额外的方法。

此外,编译器还会综合一些代码来帮助高效地实现一些所需的内容。例如,values() 方法要复制和返回的数组的源被放置在名为 $VALUES 的合成字段中。它在枚举初始化时被填充,然后每当调用 values() 时都可以复制它。

此外,为了正确初始化常量,编译器向每个构造函数添加了两个隐藏参数。因此,如果您有一个空的构造函数,那么它在幕后实际上有两个参数 - 常量的名称和序号。如果您有一个只有一个参数的构造函数,那么在幕后它将有 3 个参数。

当常量拥有自己的主体时,会创建额外的合成代码。

编译器完成的所有这些后面的实现都没有记录在 JLS 中,并且基本上由任何编译器来决定如何实现。只要枚举作为编译器生成的任何代码的结果而表现正确,就可以自由地以它选择的任何方式生成它。

<小时/>

克服您遇到的一些问题的提示:

  • 不要使用 Modifier.toString(cls.getModifiers()) 来打印 enum 修饰符,而是使用

    Modifier.toString(cls.getModifiers() & ~ Modifier.FINAL & ~ Modifier.ABSTRACT)

    这会从输出中排除 final 修饰符以及 abstract 修饰符,如果您向 enum 添加抽象方法,则会显示该修饰符(并处理常数体,这是您到目前为止尚未完成的事情)。

  • 使用Field.isSynthetic()方法检查某个字段是否由编译器合成,并且不输出该方法返回 true 的字段。这将删除 $VALUES 字段。
  • values()valueOf(String) 这两个方法不被视为合成方法,如果您对它们使用 Method.isSynthetic() ,它将返回 false。因此,您只需检查在枚举中找到的每个方法即可。如果是这两个方法之一,且签名和返回类型与 JLS 中指定的相同,则跳过它们并且不输出它们。

棘手的部分是去掉每个构造函数的两个附加参数。基本上,当您迭代构造函数参数时,可以跳过前两个参数。但是,请记住,这仅适用于 Oracle 编译器生成的代码,并且不能保证在未来版本或任何其他编译器中!

关于Java反射-枚举生成,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30832118/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com