1. 概述

在这个教程中,我们将学习如何获取字段的注解,并解释保留元注解的工作原理。随后,我们将展示两种返回字段注解的方法之间的差异。

2. 注解的保留策略

首先,让我们来看看保留策略注解。它定义了注解的生命期。这个元注解有一个RetentionPolicy属性,即属性定义了注解可见的生命周期:

  • RetentionPolicy.SOURCE:仅在源代码中可见
  • RetentionPolicy.CLASS:在编译时对编译器可见
  • RetentionPolicy.RUNTIME:对编译器和运行时都可见

因此,只有RUNTIME保留策略允许我们程序化地读取注解。

3. 使用反射获取字段的注解

现在,让我们创建一个带有注解的示例类。我们将定义三个注解,其中只有两个在运行时可见。

第一个注解在运行时可见:

@Retention(RetentionPolicy.RUNTIME)
public @interface FirstAnnotation {
}

第二个注解也有相同的保留策略:

@Retention(RetentionPolicy.RUNTIME)
public @interface SecondAnnotation {
}

最后,让我们创建一个仅在源代码中可见的第三个注解:

@Retention(RetentionPolicy.SOURCE)
public @interface ThirdAnnotation {
}

然后,让我们定义一个类,其中字段classMember带有这三个注解:

public class ClassWithAnnotations {

    @FirstAnnotation
    @SecondAnnotation
    @ThirdAnnotation
    private String classMember;
}

接下来,让我们在运行时获取所有可见的注解。我们将使用Java反射,它允许我们检查字段的属性:

@Test
public void whenCallingGetDeclaredAnnotations_thenOnlyRuntimeAnnotationsAreAvailable() throws NoSuchFieldException {
    Field classMemberField = ClassWithAnnotations.class.getDeclaredField("classMember");
    Annotation[] annotations = classMemberField.getDeclaredAnnotations();
    assertThat(annotations).hasSize(2);
}

结果,我们只获取了两个在运行时可用的注解。如果字段上没有注解,getDeclaredAnnotations方法将返回一个长度为零的数组。

我们可以以相同的方式阅读超类字段的注解:获取超类字段,然后调用同样的getDeclaredAnnotations方法。

4. 检查字段是否被特定类型注解

现在,让我们看看如何检查特定类型的注解是否存在于字段上。Field类有一个名为isAnnotationPresent的方法,当元素上存在指定类型的注解时,它会返回true。让我们在classMember字段上测试一下:

@Test
public void whenCallingIsAnnotationPresent_thenOnlyRuntimeAnnotationsAreAvailable() throws NoSuchFieldException {
    Field classMemberField = ClassWithAnnotations.class.getDeclaredField("classMember");
    assertThat(classMemberField.isAnnotationPresent(FirstAnnotation.class)).isTrue();
    assertThat(classMemberField.isAnnotationPresent(SecondAnnotation.class)).isTrue();
    assertThat(classMemberField.isAnnotationPresent(ThirdAnnotation.class)).isFalse();
}

正如预期的那样,ThirdAnnotation不存在,因为它的Retention元注解指定了SOURCE保留策略。

5. Field类的方法getAnnotationsgetDeclaredAnnotations

现在,让我们来看看Field类提供的两种方法,getAnnotationsgetDeclaredAnnotations。根据Javadoc,getDeclaredAnnotations方法返回直接存在于元素上的注解。另一方面,getAnnotations方法返回元素上存在的所有注解

类中的字段在其定义上方包含注解,因此注解继承并不涉及。所有注解都必须与字段定义一起定义。正因为如此,getAnnotationsgetDeclaredAnnotations方法始终返回相同的结果

让我们通过一个简单的测试来证明这一点:

@Test
public void whenCallingGetDeclaredAnnotationsOrGetAnnotations_thenSameAnnotationsAreReturned() throws NoSuchFieldException {
    Field classMemberField = ClassWithAnnotations.class.getDeclaredField("classMember");
    Annotation[] declaredAnnotations = classMemberField.getDeclaredAnnotations();
    Annotation[] annotations = classMemberField.getAnnotations();
    assertThat(declaredAnnotations).containsExactly(annotations);
}

此外,在Field类中,我们可以找到getAnnotations方法调用了getDeclaredAnnotations方法:

@Override
public Annotation[] getAnnotations() {
    return getDeclaredAnnotations();
}

6. 总结

在这篇短文中,我们解释了在获取注解时保留策略元注解的作用。然后我们展示了如何读取字段的注解。最后,我们证明了对于字段,注解没有继承性。

如往常一样,示例的源代码可在GitHub上获取。