如何理解 Java 注解中的直接存在、间接存在、存在和关联?

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

How to understand directly present, indirectly present, present, and associated in java annotation?

问题

AnnotatedElement的Java文档中,我看到了术语:直接存在,间接存在,存在和关联,但我不明白它们的含义。

例如,在文档中说:

如果元素E具有RuntimeVisibleAnnotations或RuntimeVisibleParameterAnnotations或RuntimeVisibleTypeAnnotations属性,并且该属性包含A,则注解A直接存在于元素E上。

但我不知道RuntimeVisibleAnnotations属性是什么,以及"属性包含A"的含义是什么。

有人能够提供一些示例来展示它们之间的差异吗?谢谢!

英文:

In java doc of AnnotatedElement, I read the term : directly present, indirectly present, present, and associated, but I couldn't understand their meaning.

For example, in the doc it says :
>An annotation A is directly present on an element E if E has a RuntimeVisibleAnnotations or RuntimeVisibleParameterAnnotations or RuntimeVisibleTypeAnnotations attribute, and the attribute contains A.

But I don't know what RuntimeVisibleAnnotations attribute is and what's the meaning of "the attribute contains A".

Can someone give some examples to show their difference, thanks!

答案1

得分: 2

属性(例如 RuntimeVisibleAnnotations

提到的属性是类文件格式的一部分。例如,RuntimeVisibleAnnotations 属性在《Java虚拟机规范》第4.7.16节中有描述:

RuntimeVisibleAnnotations 属性是 ClassFilefield_infomethod_info 结构的属性表中的可变长度属性(§4.1,§4.5,§4.6)。RuntimeVisibleAnnotations 属性记录了与相应类、字段或方法的声明上的运行时可见的注解。

ClassFilefield_infomethod_info 结构的属性表中最多可能有一个 RuntimeVisibleAnnotations 属性。

[...]

您可以通过使用 javap 来查看此属性。例如,下面的代码:

@FunctionalInterface // 具有运行时保留
public interface Foo {
  void bar(); // 满足函数式接口的要求
}

会产生以下结果:

public interface Foo
  minor version: 0
  major version: 58
  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
  this_class: #1                          // Foo
  super_class: #3                         // java/lang/Object
  interfaces: 0, fields: 0, methods: 1, attributes: 2
Constant pool:
   #1 = Class              #2             // Foo
   #2 = Utf8               Foo
   #3 = Class              #4             // java/lang/Object
   #4 = Utf8               java/lang/Object
   #5 = Utf8               bar
   #6 = Utf8               ()V
   #7 = Utf8               SourceFile
   #8 = Utf8               Foo.java
   #9 = Utf8               RuntimeVisibleAnnotations
  #10 = Utf8               Ljava/lang/FunctionalInterface;
{
  public abstract void bar();
    descriptor: ()V
    flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
}
SourceFile: "Foo.java"
RuntimeVisibleAnnotations:
  0: #10()
    java.lang.FunctionalInterface

您可以在底部看到类(接口)FooRuntimeVisibleAnnotations 属性。该属性包含一个条目:java.lang.FunctionalInterface。这意味着该注解直接存在于 Foo 上。


存在的种类

让我们假设我们有以下注解(省略导入):

@Retention(RUNTIME)
@Inherited
public @interface Foo {}
@Retention(RUNTIME)
@Inherited
@Repeatable(BarList.class)
public @interface Bar {}
@Retention(RUNTIME)
@Inherited
public @interface BarList {
  Bar[] value();
}

然后,如果我们有:

@Foo
@BarList({@Bar, @Bar})
public class Parent {}
public class Child extends Parent {}

以下是正确的:

  1. Foo 直接存在于 Parent 上。
  2. Foo 存在于 Child 上(因为它被继承了)。
  3. BarList 直接存在于 Parent 上。
  4. BarList 存在于 Child 上(因为它被继承了)。
  5. 两个 Bar 注解间接存在于 Parent 上(因为它们是可重复的,并且在它们的容器注解中)。
  6. 两个 Bar 注解与 Child 相关联(因为它们被继承,可重复,并且在它们的容器注解中)。

一些额外的注意事项:

  • 如果一个注解直接存在于 E 上,那么该注解也存在于并与 E 相关联。

  • 如果一个注解间接存在于 E 上,那么该注解也与 E 相关联。

  • 一个注解只有在满足以下条件时才会被继承:

    1. 注解类型被 java.lang.annotation.Inherited 元注解标记。
    2. 该注解存在于祖先类的上(注解继承仅适用于类,不适用于接口、方法、字段等)。
  • 当一个注解被继承,但存在于类层次结构的多个类中时,只会找到“最新”的注解(即,在查询的层次结构底部最接近的注解)。

  • 如果一个注解存在于类 E 上,但该注解不可继承,则该注解将不会存在于子类的上,并且不会与子类相关联。

  • 可重复注解不需要明确放置在其相应的容器注解中。例如,上面的示例可以这样使用:

    @Foo
    @Bar
    @Bar
    public class Parent {}
    

    两个 Bar 注解会被编译器隐式包装在它们的容器注解中(即 BarList),这意味着两个 Bar 注解仍然间接存在于 Parent 上,并且与 Child 相关联。但是,只有当可重复注解有多于一个的情况下才会发生这种隐式包装。因此,如果只有一个 Bar 注解,它将直接存在于 Parent 上并存在于 Child 上。

英文:

The Attributes (e.g. RuntimeVisibleAnnotations)

The mentioned attributes are part of the class file format. For instance, the RuntimeVisibleAnnotaitons attribute is described by §4.7.16 of the Java Virtual Machine Specification:

>The RuntimeVisibleAnnotations attribute is a variable-length attribute in the attributes table of a ClassFile, field_info, or method_info structure (§4.1,§4.5, §4.6). The RuntimeVisibleAnnotations attribute records run-time visible annotations on the declaration of the corresponding class, field, or method.
>
>There may be at most one RuntimeVisibleAnnotations attribute in the attributes table of a ClassFile, field_info, or method_info structure.
>
>[...]

You can see this attribute by inspecting the byte code via javap. For example, this:

@FunctionalInterface // has runtime retention
public interface Foo {
  void bar(); // satisfy functional interface requirements
}

Gives this:

public interface Foo
  minor version: 0
  major version: 58
  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
  this_class: #1                          // Foo
  super_class: #3                         // java/lang/Object
  interfaces: 0, fields: 0, methods: 1, attributes: 2
Constant pool:
   #1 = Class              #2             // Foo
   #2 = Utf8               Foo
   #3 = Class              #4             // java/lang/Object
   #4 = Utf8               java/lang/Object
   #5 = Utf8               bar
   #6 = Utf8               ()V
   #7 = Utf8               SourceFile
   #8 = Utf8               Foo.java
   #9 = Utf8               RuntimeVisibleAnnotations
  #10 = Utf8               Ljava/lang/FunctionalInterface;
{
  public abstract void bar();
    descriptor: ()V
    flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
}
SourceFile: "Foo.java"
RuntimeVisibleAnnotations:
  0: #10()
    java.lang.FunctionalInterface

You can see the RuntimeVisibleAnnotations attribute for the class (interface) Foo at the bottom. That attribute contains a single entry: java.lang.FunctionalInterface. That means said annotation is directly present on Foo.


Kinds of Presence

Let's assume we have the following annotations (imports omitted):

@Retention(RUNTIME)
@Inherited
public @interface Foo {}
@Retention(RUNTIME)
@Inherited
@Repeatable(BarList.class)
public @interface Bar {}
@Retention(RUNTIME)
@Inherited
public @interface BarList {
  Bar[] value();
}

Then if we have:

@Foo
@BarList({@Bar, @Bar})
public class Parent {}
public class Child extends Parent {}

The following is true:

  1. Foo is directly present on Parent.
  2. Foo is present on Child (because it's inherited)
  3. BarList is directly present on Parent
  4. BarList is present on Child (because it's inherited)
  5. Both Bar annotations are indirectly present on Parent (because they're repeatable and in their container annotation)
  6. Both Bar annotations are associated with Child (because they're inherited, repeatable, and in their container annotation)

Some additional notes:

  • If an annotation is directly present on E then the annotation is also present on and associated with E

  • If an annotation is indirectly present on E then the annotation is also associated with E

  • An annotation is inherited if and only if:

    1. The annotation type is meta-annotated with java.lang.annotation.Inherited
    2. The annotation is present on an ancestor class (annotation inheritance only applies to classes, not interfaces, methods, fields, etc.)
  • When an annotation is inherited but is present on multiple classes throughout the class hierarchy then only the "most recent" annotation is found (i.e. the annotation closest to the bottom of the queried hierarchy)

  • If an annotation is present on class E, but the annotation is not inheritable, then the annotation will not be present on nor associated with subclasses of E

  • Repeatable annotations do not need to be explicitly placed in their corresponding container annotation. For example, the above could have used:

    @Foo
    @Bar
    @Bar
    public class Parent {}
    

    The two Bar annotations are implicitly wrapped in their container annotation (i.e. BarList) by the compiler. That means the two Bar annotations are still indirectly present on Parent and associated with Child. However, this implicit wrapping only occurs if there is more than one of a repeatable annotation. So if there were only one Bar annotation then it would be directly present on Parent and present on Child.

huangapple
  • 本文由 发表于 2020年8月13日 12:19:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/63388077.html
匿名

发表评论

匿名网友

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

确定