注解的继承性

Posted by Haiming on February 27, 2023

注解的继承性主要分为以下几种:

@Inherited

@inherited 本身只能修饰其他注解,如果一个类使用了这个注解,那么他的子类就会自动继承这个注解。

看一下官方文档:

Indicates that an annotation type is automatically inherited. If an Inherited meta-annotation is present on an annotation type declaration, and the user queries the annotation type on a class declaration, and the class declaration has no annotation for this type, then the class’s superclass will automatically be queried for the annotation type. This process will be repeated until an annotation for this type is found, or the top of the class hierarchy (Object) is reached. If no superclass has an annotation for this type, then the query will indicate that the class in question has no such annotation. Note that this meta-annotation type has no effect if the annotated type is used to annotate anything other than a class. Note also that this meta-annotation only causes annotations to be inherited from superclasses; annotations on implemented interfaces have no effect.

也就是会一直沿着继承链往上找,直到找到

使用@inherited的注解,如果不是在类上使用,这个继承性就不会体现。

如果子类和父类同时使用了一个注解,那么子类也不会继承父类的注解,生效范围会是子类上面注解本身。

下面是例子:

import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Arrays;

public class AnnotationInheritanceTest {
    @Inherited
    @Target(value = ElementType.TYPE)
    @Retention(value = RetentionPolicy.RUNTIME)
    @interface AInherited{
        String value() default "";
    }


    @Target(value = ElementType.TYPE)
    @Retention(value = RetentionPolicy.RUNTIME)
    @Inherited
    @interface BInherited{
        String value() default "";
    }

    @Target(value = ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @interface  CInherited{
        String value() default "";
    }

    @AInherited("父类的AInherited")
    @BInherited("父类的BInherited")
    @CInherited("父类的CInherited")
    class SuperClass{}

    @BInherited("子类的BInherited")
    class ChildClass extends SuperClass{

    }

    public static void main(String[] args) {
        Annotation[] annotations = ChildClass.class.getAnnotations();
        System.out.println(Arrays.toString(annotations));
    }

}

输出为

[@AnnotationInheritanceTest$AInherited(value=父类的AInherited), @AnnotationInheritanceTest$BInherited(value=子类的BInherited)]

三个注解我们分别剖析:

  1. @AInherited: 这个注解在父类有,子类没有,子类直接继承了父类上面的注解
  2. @BInherited: 这个注解在父类有,在子类也有,子类没有继承父类的注解,而是直接生效了自己上面的注解
  3. @CInherited: 这个注解在父类有,但是因为注解定义时候本身没有 @Inherited ,所以不会被继承,在子类的输出上面也没看到。

继承本身(extend父类或者implement interface)

extend 父类

当子类继承父类的时候,对应的接口信息会一起继承。但是如果子类重新 @Override 了对应的方法,那么接口的注解会一起被子类覆盖掉。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;

public class AnnotationInheritanceByClass {
    @Target({ElementType.METHOD,ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @interface DESC{
        String value() default "";
    }

    class SuperClass{
        @DESC("foo")
        public void foo(){}
        @DESC("bar")
        public void bar(){}
        @DESC("field")
        public String field = "";
    }

    class ChildClass extends SuperClass{
        @Override
        public void foo(){}
    }

    public static void main(String[] args) throws NoSuchMethodException, NoSuchFieldException {
        Method foo = ChildClass.class.getMethod("foo");
        System.out.println(Arrays.toString(foo.getAnnotations()));

        Method bar = ChildClass.class.getMethod("bar");
        System.out.println(Arrays.toString(bar.getAnnotations()));

        Field field = ChildClass.class.getField("field");
        System.out.println(Arrays.toString(field.getAnnotations()));
    }

}

结果:

[]
[@AnnotationInheritanceByClass$DESC(value=bar)]
[@AnnotationInheritanceByClass$DESC(value=field)]

第一个输出拿不到注解,因为子类已经 @Override对应的方法, 所以注解同样抹掉了。但是对于其他的两个部分,可见没有问题。

implement 接口

下面是接口之中继承的例子

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;

public class AnnotationInheritanceByInterface {
    @Target({ElementType.METHOD,ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @interface DESC{
        String value() default "";
    }

    interface SuperInterface {
        @DESC("foo")
        public void foo();
        @DESC("bar")
        public void bar();
        @DESC("field")
        public String field = "";
    }

    abstract class ChildClass implements SuperInterface {
        @Override
        public void foo(){}
    }

    public static void main(String[] args) throws NoSuchMethodException, NoSuchFieldException {
        Method foo = ChildClass.class.getMethod("foo");
        System.out.println(Arrays.toString(foo.getAnnotations()));

        Method bar = ChildClass.class.getMethod("bar");
        System.out.println(Arrays.toString(bar.getAnnotations()));

        Field field = ChildClass.class.getField("field");
        System.out.println(Arrays.toString(field.getAnnotations()));
    }

}

结果:

[]
[@AnnotationInheritanceByInterface$DESC(value=bar)]
[@AnnotationInheritanceByInterface$DESC(value=field)]

注意 Override之后,也是一样,所有接口之中的注解也是消失了。

总结

@Inherited 修饰的注解,继承性只体现在对类的修饰上; 方法和属性上注解的继承,忠实于方法/属性继承本身,客观反映方法/属性上的注解。