JAVA注解的繼承性


摘要

本文從三個方面介紹java注解的**“繼承性”**:

  1. 基於元注解@Inherited,類上注解的繼承性
  2. 基於類的繼承,方法/屬性上注解的繼承性
  3. 基於接口的繼承/實現,方法/屬性上注解的繼承性

一、基於@Inherited

首先元注解@Inherited作為一個元注解,只能修飾其他注解類型(由@Target(ElementType.ANNOTATION_TYPE)決定)。

所謂的基於@Inherited的繼承性,指的是@Inherited修飾的其他注解修飾類時,這個類的子類是否可以繼承到父類的注解;主角是@Inherited修飾的其他注解,而不是@Inherited本身。

JDK中@Inherited的說明文檔很清楚的闡述了繼承性:

當用戶在一個程序元素類上,使用AnnotatedElement的相關注解查詢方法,查詢元注解Inherited修飾的其他注解類型A時,如果這個類本身並沒有被注解A修飾,那么會自動查詢這個類的父類是否被注解A修飾。查詢過程會沿着類繼承鏈一直向上查找,直到注解A被找到,或者到達繼承鏈頂層(Object)。
如果元注解Inherited修飾的其他注解,修飾了除類之外的其他程序元素,那么繼承性將會失效

下面的以demo說明:

public class ClassInheritedTest {
    @Target(value = ElementType.TYPE)
    @Retention(value = RetentionPolicy.RUNTIME)
    @Inherited // 聲明注解具有繼承性
    @interface AInherited {
        String value() default "";
    }

    @Target(value = ElementType.TYPE)
    @Retention(value = RetentionPolicy.RUNTIME)
    @Inherited // 聲明注解具有繼承性
    @interface BInherited {
        String value() default "";
    }

    @Target(value = ElementType.TYPE)
    @Retention(value = 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));
        // output: [@annotations.InheritedTest1$AInherited(value=父類的AInherited), @annotations.InheritedTest1$BInherited(value=子類的BInherited)]
    }
}

說明:

  1. 自定義注解 @CInherited 沒有被@Inherited 修飾,不具備繼承性,子類ChildClass獲取類上的注解時,沒有該注解;
  2. 自定義注解@BInherited,具備繼承性,但是子類ChildClass在類上自行指定了與父類相同類型的注解@BInherited,那么子類獲取其類注解時,@BInherited為子類自己聲明的;
  3. 自定義注解@AInherited,具備繼承性,子類上未指定相同注解,子類獲取注解時,成功獲取到父類上的@AInherited注解。

二、基於類繼承

屬性和方法注解的繼承,與類注解的繼承完全不同,與元注解Inherited毫無關系,忠實於方法/屬性本身的繼承。

以下示例說明屬性/方法注解的繼承:

public class InheritedTest {

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

    class SuperClass {
        @DESC("父類方法foo")
        public void foo() {}
        @DESC("父類方法bar")
        public void bar(){}
        @DESC("父類的屬性")
        public String field;
    }

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

    public static void main(String[] args) throws NoSuchMethodException, NoSuchFieldException {
        Method foo = ChildClass.class.getMethod("foo");
        System.out.println(Arrays.toString(foo.getAnnotations()));
        // output: []
        // 子類ChildClass重寫了父類方法foo,並且@Override注解只在源碼階段保留,所以沒有任何注解

        Method bar = ChildClass.class.getMethod("bar");
        System.out.println(Arrays.toString(bar.getAnnotations()));
        // output: [@annotations.InheritedTest$DESC(value=父類方法bar)]
        // bar方法未被子類重寫,從父類繼承到了原本注解

        Field field = ChildClass.class.getField("field");
        System.out.println(Arrays.toString(field.getAnnotations()));
    }
    // output: [@annotations.InheritedTest$DESC(value=父類的屬性)]
    // 解釋同上

三、基於接口繼承/實現

基於接口的繼承/實現中,屬性和方法注解的繼承大體與類相似。jdk7以前接口的方法都需要實現,所以子類中的方法永遠也無法獲得父接口方法的注解,但是jdk8以后的默認方法打開了這種限制。

以下以demo說明:

public class IterInheritedTest {

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

    interface SuperInterface {
        @DESC("父接口的屬性")
        String field = "field";
        @DESC("父接口方法foo")
        public void foo();
        @DESC("父接口方法bar")
        default public void bar() {

        }
    }

    interface ChildInterface extends SuperInterface {
        @DESC("子接口方法foo")
        @Override
        void foo();
    }

    class ChildClass implements SuperInterface {
        @DESC("子類的屬性")
        public String field = "field";
        @Override
        public void foo() {
        }
    }

    public static void main(String[] args) throws NoSuchMethodException, NoSuchFieldException {
        Method iFoo = ChildInterface.class.getMethod("foo");
        System.out.println(Arrays.toString(iFoo.getAnnotations()));
        // output: [@annotations.IterInheritedTest$DESC(value=子接口方法foo)]

        Method iBar = ChildInterface.class.getMethod("bar");
        System.out.println(Arrays.toString(iBar.getAnnotations()));
        // output: [@annotations.IterInheritedTest$DESC(value=父接口方法bar)]

        Field iField = ChildInterface.class.getField("field");
        System.out.println(Arrays.toString(iField.getAnnotations()));
        // output: [@annotations.IterInheritedTest$DESC(value=父接口的屬性)]

        Method foo = ChildClass.class.getMethod("foo");
        System.out.println(Arrays.toString(foo.getAnnotations()));
        // output: []; 被子類覆蓋

        Method bar = ChildClass.class.getMethod("bar");
        System.out.println(Arrays.toString(bar.getAnnotations()));
        // output: [@annotations.IterInheritedTest$DESC(value=父接口方法bar)]

        Field field = ChildClass.class.getField("field");
        System.out.println(Arrays.toString(field.getAnnotations()));
        // output: [@annotations.IterInheritedTest$DESC(value=子類的屬性)]
        // 是子類作用域下的屬性`field`
    }
}

總結

@Inherited 修飾的注解,繼承性只體現在對類的修飾上;
方法和屬性上注解的繼承,忠實於方法/屬性繼承本身,客觀反映方法/屬性上的注解。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM