Are Java annotation's default values compiled

2019-04-19 14:47发布

I try to implement several static analyses for Java bytecode. They try to compute if a certain method has a specific property, e.g. is a factory method. Because these analyses are difficult to test, I decided to write some Java code and annotate the methods directly with the correct property. After running the analysis, it is quite easy to check automatically whether the computed and the annotated property is the same.

MyAnnotation:

@Retention(RUNTIME)
@Target(METHOD)
public @interface FactoryMethodProperty {

    FactoryMethodKeys value() default FactoryMethodKeys.NonFactoryMethod;
}

Example test code:

public class PublicFactoryMethod {

    private PublicFactoryMethod(){
        // I'm private
    }

    @FactoryMethodProperty
    public static void newInstanceAfterOtherConstructorCall(){
        new TransFacoryMethod();
        new PublicFactoryMethod();
    }

    @FactoryMethodProperty(FactoryMethodKeys.IsFactoryMethod)
    public static PublicFactoryMethod newInstance(){
        return new PublicFactoryMethod();
    }
}

Because most of the methods in my test code are no factory methods, I set the default to the enum value "FactoryMethodKeys.NonFactoryMethod". But when I don't explicitly pass the enum value to the annotation, it is not compiled to the bytecode.

Bytecode:

 #23 = Utf8               value
  #24 = Utf8               Lorg/opalj/fpa/test/annotations/FactoryMethodKeys;
  #25 = Utf8               IsFactoryMethod

{
  public static void newInstanceAfterOtherConstructorCall();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC
    RuntimeVisibleAnnotations:
      0: #16()
    Code:
      stack=1, locals=0, args_size=0
         0: new           #17                 // class factoryMethodTest/TransFacoryMethod
         3: invokespecial #19                 // Method factoryMethodTest/TransFacoryMethod."<init>":()V
         6: new           #1                  // class factoryMethodTest/PublicFactoryMethod
         9: invokespecial #20                 // Method "<init>":()V
        12: return
      LineNumberTable:
        line 49: 0
        line 50: 6
        line 51: 12
      LocalVariableTable:
        Start  Length  Slot  Name   Signature

  public static factoryMethodTest.PublicFactoryMethod newInstance();
    descriptor: ()LfactoryMethodTest/PublicFactoryMethod;
    flags: ACC_PUBLIC, ACC_STATIC
    RuntimeVisibleAnnotations:
      0: #16(#23=e#24.#25)
    Code:
      stack=2, locals=0, args_size=0
         0: new           #1                  // class factoryMethodTest/PublicFactoryMethod
         3: dup
         4: invokespecial #20                 // Method "<init>":()V
         7: areturn
      LineNumberTable:
        line 55: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
}

What did I wrong? Why is the default value completely ignored?

2条回答
Deceive 欺骗
2楼-- · 2019-04-19 14:59

If you take a look for the bytecode for the annotation, you'll see the default there. Using javap -c -v and trimming the irrelevant things:

...
ConstantPool:
  #7 = Utf8               LFactoryMethodKeys
  #8 = Utf8               NonFactoryMethod
...
{
  public abstract FactoryMethodKeys value();
    flags: ACC_PUBLIC, ACC_ABSTRACT
    AnnotationDefault:
      default_value: e#7.#8}
查看更多
祖国的老花朵
3楼-- · 2019-04-19 15:10

It doesn't need to be there. At runtime, the JVM constructs an annotation instance that you can retrieve. This instance will be initialized with the default value which is in the .class file for the annotation itself. That's represented as an AnnotationDefault attribute

The AnnotationDefault attribute is a variable-length attribute in the attributes table of certain method_info structures (§4.6), namely those representing elements of annotation types. TheAnnotationDefault attribute records the default value for the element represented by the method_info structure.

Each method_info structure representing an element of an annotation type may contain at most one AnnotationDefault attribute. The Java Virtual Machine must make this default value available so it can be applied by appropriate reflective APIs.

You'll eventually invoke the annotation instance's value() method (or whatever other method you defined) and it will return that value.

查看更多
登录 后发表回答