1

I make program which can generate source code of class,enum and interfaces using reflection but I have problem with Enum generating.

My EumTest class

public enum EnumTest{
a,b;

private  String r;

private EnumTest(){

}

private void some(){

}

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

Method which generate enum file

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

Result is this new Enum

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

}

As you can see there are som errors. For example Modifier generate FINAL and enum cant be final, next there are more methods and fields, constructor has paramaters ...

Constructor generate

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

Field generate

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

Methods generate

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

If you have any ideas how to get enum which can be compiled.

2
  • Where did the daco come from? Commented Jun 14, 2015 at 18:02
  • sry i copy it and rename on stackoverflow some2 is daco but i added [] dont know why i will change it Commented Jun 14, 2015 at 18:16

1 Answer 1

1

Here is an explanation of why you get all the extra stuff.

Enums are implemented using a lot of code that is synthesized by the compiler, so that "behind the scenes" they behave as the Java Language Spec specifies, without requiring the JVM to be drastically changed.

The Java Language Specification says, for example, that:

  • An enum is implicitly final unless there's a constant with a body.
  • Any enum implicitly declares the methods values() and valueOf(String).

For this reason, you got the final modifier for the EnumTest, and you got the two extra methods.

In addition, the compiler synthesizes some code to help implement some of the required stuff efficiently. For example, the source of the array to be copied and returned by the method values() is placed in a synthetic field named, in this case, $VALUES. It is filled up at the initialization of the enum, and then it can be copied whenever values() is called.

Also, to properly initialize the constants, the compiler adds two hidden parameters to each of the constructors. So if you have an empty constructor, behind the scenes it actually has two parameters - the constant's name and its ordinal number. If you have a constructor with one parameter, behind the scenes it will have 3 parameters.

There is additional synthetic code created when you have constants with their own bodies.

All of these latter implementations done by the compiler are not documented in the JLS and are basically left up to any compiler to decide how to implement. As long as the enum behaves correctly as a result of whatever code the compiler generates, it is free to generate it in any way it chooses.


Tips for overcoming some of the problems you have encountered:

  • Instead of using Modifier.toString(cls.getModifiers()) to print the enum modifiers, use

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

    This excludes the final modifier from the output as well as the abstract modifier which will show up if you add abstract methods to the enum (and process the constant bodies, which is something you haven't done so far).

  • Use the Field.isSynthetic() method to check whether a field is synthesized by the compiler, and do not output the fields for which this method returns true. This will get rid of the $VALUES field.
  • The two methods values() and valueOf(String) are not considered synthetic and if you use Method.isSynthetic() on them, it will return false. So you'll simply have to check each method you find in the enum. If it is one of these two methods, with the signature and return type as specified in the JLS, skip them and do not output them.

The tricky part is getting rid of the two additional parameters of each of the constructors. Basically, when you iterate through the constructor parameters, you can skip the first two parameters. However, remember that this will only work for code generated by the Oracle compiler and is not guaranteed in future versions or any other compiler!

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.