@Inherited是Java中唯一影响注解继承的元注解,仅对类层级直接继承有效,不适用于方法、字段、接口实现或重写方法;自定义注解必须显式声明@Inherited且仅作用于ElementType.TYPE,否则子类无法通过反射获取父类该注解。

@Inherited 是 Java 中唯一一个能影响注解继承行为的元注解,但它只对类层级的注解继承有效,且有严格限制——它不适用于方法、字段或参数上的注解,也不传递到实现类(接口)或子类的重写方法上。很多开发者误以为加了 @Inherited 就能“自动继承所有注解”,结果在反射获取时发现子类拿不到父类的注解,根本原因在于对它的作用范围和语义理解偏差。
只作用于类,且仅限 直接继承 的 class
@Inherited 只在子类显式 extends 父类,且父类使用了被 @Inherited 标记的注解时,子类通过 getAnnotation(Class) 才可能获取到该注解。它不适用于接口实现(implements),即使接口上有 @Inherited 注解,实现类也完全无法继承。
- ✅ 正确场景:父类
@MyAnno(该注解声明了 @Inherited),子类extends Parent→ 子类可反射读到@MyAnno - ❌ 错误假设:接口
@MyAnno interface IService,实现类class ServiceImpl implements IService→ServiceImpl.class.getAnnotation(MyAnno.class)返回 null - ❌ 错误假设:父类方法上有
@MyAnno,子类重写该方法 → 子类方法上 不会自动拥有 该注解
@Inherited 不触发“深度传递”,仅限一级继承
如果 A 类标注了 @Inherited 注解,B 类继承 A,C 类继承 B,那么 C 类能拿到该注解,是因为继承链最终追溯到了 A —— 这是反射机制沿继承链向上查找的结果,不是 @Inherited 自身做了多级传播。但关键点在于:只要中间任意一环的类自身未声明该注解,且其注解类型没加 @Inherited,整个链就断掉。
- 例如:
@Inherited @interface Conf;@Conf class A;class B extends A;class C extends B→ C 可 getAnnotation(Conf.class) - 但如果
class B自己也加了@Conf,而这个注解类型没加 @Inherited → 那么 C 就无法从 B 继承该注解(因 B 的注解不可继承),只能靠 A 传递;若 A 也没加 @Inherited,则 C 拿不到
替代方案:手动扫描继承链
当需要真正“跨层级”或“跨接口”获取语义相关注解(比如权限校验、配置注入),不能依赖 @Inherited,而应编写通用工具方法,主动遍历 Class.getSuperclass() 和 Class.getInterfaces(),逐层检查注解存在性。
立即学习“Java免费学习笔记(深入)”;
- 用递归或循环获取所有超类(直到 Object)和全部接口(含间接接口,需展开 getInterfaces() 的每个元素)
- 对每个 Class 调用
getDeclaredAnnotation()或getAnnotation(),优先用前者避免父类同名注解干扰 - 注意:接口默认方法上的注解不可被实现类继承,字段/构造器注解也一律不继承 —— 这些都需单独设计规则处理
自定义注解必须显式声明 @Inherited 才生效
很多人写了自定义注解却忘了加 @Inherited,导致即使类继承关系正确,子类依然读不到。记住:@Inherited 不是默认开启的,它是 opt-in 机制。
- 错误写法:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface MyConfig { ... }→ 不可继承 - 正确写法:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface MyConfig { ... }→ 仅对类有效 - 提醒:@Inherited 本身只能修饰注解类型(ElementType.ANNOTATION_TYPE),不能加在普通类/方法上
不复杂但容易忽略。真正可靠的注解语义传递,往往得靠约定 + 工具类 + 显式声明,而不是寄希望于一个名字带“Inherited”的元注解包打全场。


















