Java反射 - 枚举生成

时间:2015-06-14 16:59:09

标签: java reflection enums

我制作的程序可以使用反射生成类,枚举和接口的源代码,但是我对Enum生成有问题。

我的EumTest课程

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;
}

}

正如您所看到的,有som错误。例如,Modifier生成FINAL和枚举不能是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);
}

如果你有任何想法如何获得可以编译的枚举。

1 个答案:

答案 0 :(得分:1)

以下是您获得所有额外资料的原因。

枚举是使用由编译器合成的大量代码实现的,因此&#34;在幕后&#34;它们的行为与Java语言规范指定的一样,不需要对JVM进行彻底更改。

例如,Java Language Specification表示:

  • enum隐含final,除非有身体的常量。
  • 任何enum隐式声明方法values()valueOf(String)

出于这个原因,你获得了final的{​​{1}}修饰符,你得到了两个额外的方法。

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

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

当您拥有自己的实体常量时,会创建其他合成代码。

编译器完成的所有这些后续实现都是在JLS中记录的,并且基本上由任何编译器决定如何实现。只要enum由于编译器生成的任何代码而正确运行,就可以以它选择的任何方式自由生成它。

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

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

    enum

    这将排除输出中的Modifier.toString(cls.getModifiers() & ~ Modifier.FINAL & ~ Modifier.ABSTRACT) 修饰符以及final修饰符,如果您向abstract添加抽象方法(并处理常量实体,则会显示)你到目前为止还没做过的事情。)

  • 使用Field.isSynthetic()方法检查字段是否由编译器合成,并且不输出此方法返回enum的字段。这将摆脱true字段。
  • 两种方法$VALUESvalues()不属于合成方法,如果对它们使用valueOf(String),则会返回false。因此,您只需检查枚举中找到的每种方法即可。如果它是这两种方法之一,使用JLS中指定的签名和返回类型,请跳过它们,不要输出它们。

棘手的部分是摆脱每个构造函数的两个附加参数。基本上,当您遍历构造函数参数时,可以跳过前两个参数。但是,请记住,这只适用于Oracle编译器生成的代码,并且在将来的版本或任何其他编译器中无法保证