英文:
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:
Foo
is directly present onParent
.Foo
is present onChild
(because it's inherited)BarList
is directly present onParent
BarList
is present onChild
(because it's inherited)- Both
Bar
annotations are indirectly present onParent
(because they're repeatable and in their container annotation) - Both
Bar
annotations are associated withChild
(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 withE
-
If an annotation is indirectly present on
E
then 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
Bar
annotations are implicitly wrapped in their container annotation (i.e.BarList
) by the compiler. That means the twoBar
annotations are still indirectly present onParent
and associated withChild
. However, this implicit wrapping only occurs if there is more than one of a repeatable annotation. So if there were only oneBar
annotation then it would be directly present onParent
and present onChild
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论