在Java标记为@Inhered
的注释仅在注释类时有效:
请注意,如果注释类型用于注释类以外的任何内容,则此元注释类型无效。另请注意,此元注释仅导致注释从超类继承;对已实现接口的注释没有影响。
因此,使用@Inhered
注释注释的接口或方法不会导致实现也使用注释注释的类/方法。这样做的原因很可能是,如果类层次结构中有多个注释,编译器将不知道选择哪个注释。
现在Java8引入了新的注解@Repeable
。我认为对于既标记为@继承
又标记为@可重复
的注解,取消上述限制是很自然的,因为编译器应该能够将冲突的注解添加到@可重复
注解中。
给出以下示例:
import java.lang.annotation.Inherited;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
@interface RepeatableAnnotations {
RepeatableAnnotation[] value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
@Repeatable(RepeatableAnnotations.class)
@interface RepeatableAnnotation {
String value();
}
@RepeatableAnnotation("A")
interface IntefaceA {}
@RepeatableAnnotation("B")
interface IntefaceB {}
@RepeatableAnnotation("C")
@RepeatableAnnotation("D")
public class TestClass implements IntefaceA, IntefaceB {
public static void main(String[] args) {
for (RepeatableAnnotation a : TestClass.class.getAnnotation(RepeatableAnnotations.class).value()) {
System.out.print(a.value());
}
}
}
我希望输出是ABCD
,但它只是CD
(即@继承
的工作方式与前Java8完全相同)。
有人知道在Java8的@Repeable
注释的情况下,是否有充分的理由不删除关于接口和方法的@继承
限制?
是否有任何解决方法来实现上述类型层次结构的ABCD
输出?(除了使用反射扫描超级接口以获取注释…)
请回忆@Inhered
的留档:
如果注解类型声明上存在继承的元注解,并且用户在类声明上查询注解类型,并且类声明没有该类型的注解,则会自动查询该类的超类的注解类型。
换句话说,@Inhered
从未打算成为在类型层次结构上收集多个注释的功能。相反,您将获得具有显式注释的最特定类型的注释。
换句话说,如果你把你的声明改成
@RepeatableAnnotation("FOO") @RepeatableAnnotation("BAR") class Base {}
@RepeatableAnnotation("C") @RepeatableAnnotation("D")
public class TestClass extends Base implements IntefaceA, IntefaceB {
它不会改变结果;Base
的FOO
和BAR
不会被TestClass
继承,因为它具有显式注释值C
和D
。
将其扩展到接口
层次结构会很尴尬,因为多重继承以及一个超级接口可能是另一个超级接口的子接口的事实,因此找到最具体的接口并不简单。这与超类层次结构的线性搜索有很大不同。
您可能会遇到存在多个不相关的注释接口
的情况,但不清楚为什么应该通过将它们连接到一个重复的注释中来解决这种歧义。这不会与所有其他场景中的行为相协调。
请注意,您链接的答案有点奇怪,因为它显示了使用方法注释的代码,但是方法注释永远不会被继承,无论您是否指定了@Inhered
(当您将@Target(ElementType. METHOD)
与@Inhered
结合时,审核工具应该生成警告,imho)。@Inhered
仅与类型注释相关。
但是我发现这个线程在寻找类似的解决方案。最后我写了这个辅助方法:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Labels
{
Label[] value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Repeatable(Labels.class)
public @interface Label
{
String value();
}
@Label("A")
class A
{
}
@Label("B")
class B extends A
{
}
@Test
void LabelStackTest()
{
var labels = ClassUtils.getAnnotatedLabels(B.class);
assertThat(labels).contains("A");
assertThat(labels).contains("B");
}
public static List<String> getAnnotatedLabels(Class<?> labeledClass)
{
var labels = new ArrayList<String>();
do
{
labels.addAll(Arrays.asList(labeledClass.getAnnotationsByType(Label.class))
.stream()
.map(labelAnnotations -> labelAnnotations.value())
.toList());
labeledClass = labeledClass.getSuperclass();
} while (labeledClass != Object.class);
return labels;
}