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?
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: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 anAnnotationDefault
attributeYou'll eventually invoke the annotation instance's
value()
method (or whatever other method you defined) and it will return that value.