英文:
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属性是ClassFile、field_info或method_info结构的属性表中的可变长度属性(§4.1,§4.5,§4.6)。RuntimeVisibleAnnotations属性记录了与相应类、字段或方法的声明上的运行时可见的注解。
在 ClassFile、field_info 或 method_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
您可以在底部看到类(接口)Foo的 RuntimeVisibleAnnotations 属性。该属性包含一个条目: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 {}
以下是正确的:
Foo直接存在于Parent上。Foo存在于Child上(因为它被继承了)。BarList直接存在于Parent上。BarList存在于Child上(因为它被继承了)。- 两个
Bar注解间接存在于Parent上(因为它们是可重复的,并且在它们的容器注解中)。 - 两个
Bar注解与Child相关联(因为它们被继承,可重复,并且在它们的容器注解中)。
一些额外的注意事项:
-
如果一个注解直接存在于
E上,那么该注解也存在于并与E相关联。 -
如果一个注解间接存在于
E上,那么该注解也与E相关联。 -
一个注解只有在满足以下条件时才会被继承:
- 注解类型被
java.lang.annotation.Inherited元注解标记。 - 该注解存在于祖先类的类上(注解继承仅适用于类,不适用于接口、方法、字段等)。
- 注解类型被
-
当一个注解被继承,但存在于类层次结构的多个类中时,只会找到“最新”的注解(即,在查询的层次结构底部最接近的注解)。
-
如果一个注解存在于类
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:
Foois directly present onParent.Foois present onChild(because it's inherited)BarListis directly present onParentBarListis present onChild(because it's inherited)- Both
Barannotations are indirectly present onParent(because they're repeatable and in their container annotation) - Both
Barannotations are associated withChild(because they're inherited, repeatable, and in their container annotation)
Some additional notes:
-
If an annotation is directly present on
Ethen the annotation is also present on and associated withE -
If an annotation is indirectly present on
Ethen the annotation is also associated withE -
An annotation is inherited if and only if:
- The annotation type is meta-annotated with
java.lang.annotation.Inherited - The annotation is present on an ancestor class (annotation inheritance only applies to classes, not interfaces, methods, fields, etc.)
- The annotation type is meta-annotated with
-
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 ofE -
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
Barannotations are implicitly wrapped in their container annotation (i.e.BarList) by the compiler. That means the twoBarannotations are still indirectly present onParentand associated withChild. However, this implicit wrapping only occurs if there is more than one of a repeatable annotation. So if there were only oneBarannotation then it would be directly present onParentand present onChild.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论