bigywater.tech
Java · 284

注解继承与组合

子类能看到父类上的注解吗?覆盖的方法呢?接口上的呢?@Repeatable 又加了 @Inherited 会怎样?这篇用反射把每种组合都实际打印出来。

3 种
不继承注解的位置:方法、参数、接口
[c] 与 [Tags, Tag]
子类自己声明一个可重复注解时,getAnnotationsByType 与 getAnnotations 的不同结果
Run / Cls / Src
分别出现在 RuntimeVisible、RuntimeInvisible、不出现在 class 文件

类上的 @Inherited

实测 · 本机执行输出
    Parent      getAnnotations=[@Inh("I-parent"), @Plain("P-parent")]
                getDeclaredAnnotations=[@Inh("I-parent"), @Plain("P-parent")]
    Child       getAnnotations=[@Inh("I-parent")]
                getDeclaredAnnotations=[]
    GrandChild  getAnnotations=[@Inh("I-parent")]
                getDeclaredAnnotations=[]
    Override    getAnnotations=[@Inh("I-child"), @Plain("P-child")]
                getDeclaredAnnotations=[@Inh("I-child"), @Plain("P-child")]
    Impl        getAnnotations=[]
                getDeclaredAnnotations=[]
    Child.isAnnotationPresent(Plain)=false  isAnnotationPresent(Inh)=true
    Child.getAnnotation(Inh).value()=I-parent
    Impl.isAnnotationPresent(Inh)(接口上的 @Inherited 注解)=false

方法、字段与参数

实测 · 本机执行输出
    Base.run 注解:[@Main.M("base"), @Main.Inh("base-m")]
    Sub.run(覆盖了 run) 注解:[] 声明类=Sub
    Sub2.run(没覆盖) 声明类=Base 注解个数=2
    Sub.run 的参数注解:[[]];Base.run 的参数注解:[[@Main.M("p-base")]]
    Base.run 上能看到接口 Svc.run 的 @M("svc") 吗:Base 注解里 M.value=base
    Sub.field(继承来的字段)注解:[@Main.M("f")]
      向上找 Sub.run → 注解 0 个
      向上找 Base.run → 注解 2 个
    @Inherited 标在方法注解上:Base.run 的 Inh 被 Sub.run 继承了吗 = false

可重复注解

实测 · 本机执行输出
    P  getAnnotationsByType(Tag)=[a, b]   getDeclaredAnnotationsByType=[a, b]   getAnnotations 类型=[Tags]
    C  getAnnotationsByType(Tag)=[a, b]   getDeclaredAnnotationsByType=[]       getAnnotations 类型=[Tags]
    D  getAnnotationsByType(Tag)=[c]      getDeclaredAnnotationsByType=[c]      getAnnotations 类型=[Tags, Tag]
    E  getAnnotationsByType(Tag)=[only]   getDeclaredAnnotationsByType=[only]   getAnnotations 类型=[Tags, Tag]
    F  getAnnotationsByType(Tag)=[x, y]   getDeclaredAnnotationsByType=[x, y]   getAnnotations 类型=[Tags]
    P 上 getAnnotation(Tag)(多个重复时)=null;getAnnotation(Tags)=@Tags({@Tag("a"), @Tag("b")})
    D 上 getAnnotation(Tag)=c(只有一个 @Tag 时,单个形式直接可见)

保留策略与 class 文件

实测 · 本机执行输出
  T 上反射可见:[@Main.Run()]
  T.m 上反射可见:[@Main.Run()]
  javap 里 Main$T 的注解属性:
     #12 = Utf8 RuntimeVisibleAnnotations
     #14 = Utf8 RuntimeInvisibleAnnotations
     RuntimeVisibleAnnotations:
     0: #13()
     RuntimeInvisibleAnnotations:
     0: #15()
     RuntimeVisibleAnnotations:
     0: #13()
     RuntimeInvisibleAnnotations:
     0: #15()
  常量池里出现的注解类型名: ['Cls', 'Run']

分步演示

注解继承
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12,普通反射 API
    • 没有验证:Spring 的 AnnotatedElementUtils 等「合并注解」工具、类型注解(TYPE_USE)、注解处理器、模块化环境、JDK 其它版本的行为差异、D 例中继承到容器的原因只是观察,没有查看 JDK 源码
    京ICP备2026067502号-1京公网安备11010502063487号