为什么 `getDeclaredAnnotations()` 在存在注解时返回空列表?

huangapple go评论73阅读模式
英文:

Why is `getDeclaredAnnotations()` returning an empty list when an annotation is present?

问题

以下是翻译好的内容:

Javadocs中关于getDeclaredAnnotations方法的条目中提到:

> 返回直接存在于此元素上的注解。此方法忽略继承的注解。如果在此元素上没有直接存在的注解,则返回值是长度为0的数组。调用此方法的调用者可以修改返回的数组,这不会对返回给其他调用者的数组产生影响。

因此,我期望这个函数在doSomething上返回一个长度为1的数组,但实际上它返回一个长度为0的数组。为什么?与相关类型的getAnnotation方法也返回null

MCVE(最小可重现示例):

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

public class AnnotationTest {

    public static void main(String[] args) {
        Class<?> clazz = AnnotationTest.class;
        for (Method method : clazz.getMethods()) {
            System.out.println(method.getName() + ":");
            for (Annotation annotation : method.getDeclaredAnnotations()) {
                System.out.println(" - " + annotation.annotationType().getName());
            }
            System.out.println();
        }
    }

    @ExampleAnnotation
    public void doSomething() {}

    public @interface ExampleAnnotation {}
}

实际的MCVE输出:

main:

doSomething:

wait:

wait:

wait:

equals:

toString:

hashCode:
 - jdk.internal.HotSpotIntrinsicCandidate

getClass:
 - jdk.internal.HotSpotIntrinsicCandidate

notify:
 - jdk.internal.HotSpotIntrinsicCandidate

notifyAll:
 - jdk.internal.HotSpotIntrinsicCandidate

预期的MCVE输出:

// 不显示不相关的方法条目

doSomething:
 - AnnotationTest.ExampleAnnotation

// 不显示不相关的方法条目
英文:

The Javadocs entry for getDeclaredAnnotations say the following:

> Returns annotations that are directly present on this element. This method ignores inherited annotations. If there are no annotations directly present on this element, the return value is an array of length 0. The caller of this method is free to modify the returned array; it will have no effect on the arrays returned to other callers.

So, I expect this function to return an array of length 1 on doSometing, yet, it returns an array of length 0. Why? getAnnotation for the relevant type also returns null.

MCVE:

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

public class AnnotationTest{

    public static void main(String[] args){
        Class&lt;?&gt; clazz = AnnotationTest.class;
        for(Method method : clazz.getMethods()){
            System.out.println(method.getName() + &quot;:&quot;);
            for(Annotation annotation : method.getDeclaredAnnotations()){
                System.out.println(&quot; - &quot; + annotation.annotationType().getName());
            }
            System.out.println();
        }
    }

    @ExampleAnnotation
    public void doSomething(){}

    public @interface ExampleAnnotation{}

}

Actual MCVE output:

main:

doSomething:

wait:

wait:

wait:

equals:

toString:

hashCode:
 - jdk.internal.HotSpotIntrinsicCandidate

getClass:
 - jdk.internal.HotSpotIntrinsicCandidate

notify:
 - jdk.internal.HotSpotIntrinsicCandidate

notifyAll:
 - jdk.internal.HotSpotIntrinsicCandidate

Expected MCVE output:

// irrelevant method entries aren&#39;t shown

doSomething:
 - AnnotationTest.ExampleAnnotation

// irrelevant method entries aren&#39;t shown

答案1

得分: 5

根据Java语言规范§9.6.4.2

> 如果类型 T 没有与 java.lang.annotation.Retention 对应的(元)注解 m,则Java编译器必须将类型 T 视为具有这样一个带有元素值为 java.lang.annotation.RetentionPolicy.CLASS 的元注解 m

您的 ExampleAnnotation 没有 @Retention 元注解,因此它只会保留在类文件中。然而,为了能够通过反射访问它,它必须在运行时保留。根据 RetentionPolicy.RUNTIME文档

> 注解应该由编译器记录在类文件中,并由虚拟机在运行时保留,因此可以通过反射方式读取

CLASS 的描述进行对比:

> 注解应该由编译器记录在类文件中,但虚拟机在运行时不需要保留。

所以要获得您期望的输出,您应该像这样声明您的 ExampleAnnotation

@Retention(RetentionPolicy.RUNTIME)
@interface ExampleAnnotation{}
英文:

According to the Java Language Specification §9.6.4.2,

> If T does not have a (meta-)annotation m that corresponds to java.lang.annotation.Retention, then a Java compiler must treat T as if it does have such a meta-annotation m with an element whose value is java.lang.annotation.RetentionPolicy.CLASS.

Your ExampleAnnotation does not have a @Retention meta-annotation, so it is only retained in the class file. However, it must be retained at runtime in order to be accessed with reflection. From the docs of RetentionPolicy.RUNTIME:

> Annotations are to be recorded in the class file by the compiler and retained by the VM at run time, so they may be read reflectively.

Compare that with the description for CLASS:

> Annotations are to be recorded in the class file by the compiler but need not be retained by the VM at run time.

So to get your desired output, you should declare your ExampleAnnotation like this:

@Retention(RetentionPolicy.RUNTIME)
@interface ExampleAnnotation{}

huangapple
  • 本文由 发表于 2020年8月26日 10:05:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/63589479.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定