提问者:小点点

为什么@可重复注释不能从接口继承


在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输出?(除了使用反射扫描超级接口以获取注释…)


共2个答案

匿名用户

请回忆@Inhered的留档:

如果注解类型声明上存在继承的元注解,并且用户在类声明上查询注解类型,并且类声明没有该类型的注解,则会自动查询该类的超类的注解类型。

换句话说,@Inhered从未打算成为在类型层次结构上收集多个注释的功能。相反,您将获得具有显式注释的最特定类型的注释。

换句话说,如果你把你的声明改成

@RepeatableAnnotation("FOO") @RepeatableAnnotation("BAR") class Base {}

@RepeatableAnnotation("C") @RepeatableAnnotation("D")
public class TestClass extends Base implements IntefaceA, IntefaceB {

它不会改变结果;BaseFOOBAR不会被TestClass继承,因为它具有显式注释值CD

将其扩展到接口层次结构会很尴尬,因为多重继承以及一个超级接口可能是另一个超级接口的子接口的事实,因此找到最具体的接口并不简单。这与超类层次结构的线性搜索有很大不同。

您可能会遇到存在多个不相关的注释接口的情况,但不清楚为什么应该通过将它们连接到一个重复的注释中来解决这种歧义。这不会与所有其他场景中的行为相协调。

请注意,您链接的答案有点奇怪,因为它显示了使用方法注释的代码,但是方法注释永远不会被继承,无论您是否指定了@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;
}