@inherited仅使类声明上的自定义注解可被子类反射获取,需同时满足:注解自身标注@inherited、作用于父类声明、子类直接继承且未重写该注解;对方法、接口、内置注解无效。

@Inherited 是 Java 中一个特殊的元注解,但它**只对类级别的注解生效,且仅支持“继承”到子类的类声明上,不作用于方法、字段或接口**。它并不能让子类“自动获得”父类的所有注解,而是限定:当一个类被标记了带有 @Inherited 的自定义注解时,它的子类在调用 getAnnotations() 或 getAnnotation(Class) 时,**可能**(取决于 JVM 实现和注解使用方式)看到该注解——但前提是父类显式声明了该注解,且该注解自身标注了 @Inherited。
必须满足的三个前提条件
要让子类通过反射“看到”父类的注解,需同时满足:
- 自定义注解本身必须用
@Inherited修饰(即元注解); - 该注解必须作用在父类的 类声明 上(不能是方法、字段、构造器);
- 子类必须是 直接继承(
extends)该父类,且未在子类上重复声明同名注解(否则会覆盖)。
@Inherited 不生效的常见误解
很多人误以为加了 @Inherited 就能让子类“继承”任意注解,但实际上:
- 它对
@Override、@Deprecated等内置注解无效(它们没加@Inherited); - 它对实现接口(
implements)完全无效——接口上的注解永远不会被实现类继承; - 它对方法上的注解无效:即使父类方法有带
@Inherited的注解,子类重写后调用getMethod(...).getAnnotation(...)也拿不到; - 它不触发编译期行为,也不改变运行时逻辑——只是影响反射 API 是否返回该注解。
一个可验证的正确示例
定义一个可继承的注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface MyConfig {
String value() default "";
}
父类使用它:
@MyConfig("parent")
public class Parent { }
子类继承:
public class Child extends Parent { }
测试获取:
System.out.println(Child.class.getAnnotation(MyConfig.class)); // 输出:@MyConfig(value="parent") —— 成功获取
但如果把 @MyConfig 加在 Parent 的某个方法上,Child.class.getMethod("xxx").getAnnotation(MyConfig.class) 一定为 null。
替代方案:手动扫描父类注解
因 @Inherited 能力有限,实际开发中更可靠的方式是**主动遍历继承链**:
- 用
Class#getSuperclass()循环向上查找,对每个父类调用getAnnotation(); - 结合
AnnotationUtils(Spring 提供)或自定义工具类,统一提取类及其所有父类的指定注解; - 若需支持接口默认方法或组合注解(
@AliasFor),更要绕过@Inherited依赖反射遍历。
不复杂但容易忽略
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











