Значения по умолчанию аннотации Java, скомпилированные в байт-код?

Я пытаюсь реализовать несколько статических анализов для байт-кода Java. Они пытаются вычислить, имеет ли определенный метод определенное свойство, например. является методом factory. Поскольку эти анализы трудно проверить, я решил написать некоторый Java-код и аннотировать методы непосредственно с правильным свойством. После выполнения анализа довольно легко проверить, является ли вычисленное и аннотированное свойство одинаковым.

MyAnnotation:

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

    FactoryMethodKeys value() default FactoryMethodKeys.NonFactoryMethod;
}

Пример тестового кода:

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

Поскольку большинство методов в моем тестовом коде не являются методами factory, я устанавливал значение по умолчанию для значения перечисления "FactoryMethodKeys.NonFactoryMethod". Но когда я явно не передаю значение enum в аннотацию, он не компилируется в байт-код.

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
}

Что я не так понял? Почему значение по умолчанию полностью игнорируется?

Ответ 1

Этого не должно быть. Во время выполнения JVM создает экземпляр аннотации, который вы можете получить. Этот экземпляр будет инициализирован значением default, которое находится в файле .class для самой аннотации. Это представлено как атрибут AnnotationDefault

Атрибут AnnotationDefault является атрибутом переменной длины в таблицы атрибутов определенных структур method_info (§4.6), а именно: те , представляющие элементы типов аннотаций. AnnotationDefaultатрибут записывает значение по умолчанию для элемента, представленного method_info.

Каждая структура method_info, представляющая элемент аннотации тип может содержать не более одного атрибута AnnotationDefault. Java Виртуальная машина должна сделать это значение по умолчанию доступным, чтобы оно могло быть применяемые соответствующими отражающими API.

В конечном итоге вы вызовете метод экземпляра аннотации value() (или какой-либо другой метод, который вы определили), и он вернет это значение.

Ответ 2

Если вы посмотрите на байт-код для аннотации, вы увидите там по умолчанию. Используя javap -c -v и обрезая нерелевантные вещи:

...
ConstantPool:
  #7 = Utf8               LFactoryMethodKeys
  #8 = Utf8               NonFactoryMethod
...
{
  public abstract FactoryMethodKeys value();
    flags: ACC_PUBLIC, ACC_ABSTRACT
    AnnotationDefault:
      default_value: e#7.#8}