2015-06-14 137 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編譯器生成的代碼,在將來的版本或任何其他編譯器中不能保證!