2015-06-14 121 views
1

我做的程序可以生成类,枚举和接口的源代码使用反射,但我有问题与枚举生成。Java反射 - 枚举生成

我EumTest类生成枚举文件

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("}"); 
} 

结果

public enum EnumTest{ 
a,b; 

private String r; 

private EnumTest(){ 

} 

private void some(){ 

} 

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

方法是这样的新枚举

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

} 

正如你可以看到有索姆错误。例如修改生成最终的枚举不能是最终的,未来有更多的方法和字段,构造函数PARAMATERS ...

构造产生

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

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

+0

'daco'从哪里来? – RealSkeptic

+0

sry我复制它,并重命名在stackoverflow some2是达科但我补充说[]不知道为什么我会改变它 – salian

回答

1

下面是为什么你得到所有额外的东西的解释。

使用大量由编译器合成的代码来实现枚举,以便在“幕后”按照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编译器生成的代码,在将来的版本或任何其他编译器中不能保证!