Java中注解是否可以继承
- @Inherited
- 基本概念
- 使用场景
- 注意事项
- 实体类
- 自定义注解
- 测试方法
- 运行结果
- 使用@Inherited
- 不使用@Inherited
- 结论
在解决这个问题之前需要先了解一下@Inherited
@Inherited
基本概念
@Inherited
是Java中的一个元注解,位于java.lang.annotation
包内,它用于修饰自定义注解,表明这个自定义注解具有继承性。这意味着,如果一个类应用了带有@Inherited的注解,那么它的子类在不显式添加这个注解的情况下,也会被视为拥有这个注解。然而,重要的是要理解**@Inherited的继承特性仅局限于类级别的注解,并不适用于方法、字段、构造器等成员级别的注解**。
使用场景
标记接口实现
:如果你定义了一套标记接口,希望实现这些接口的类自动具有某种特征或标记,可以创建一个带有@Inherited的注解来实现这一需求。框架配置
:在构建框架或库时,可能需要定义一些配置注解来指导框架如何处理特定类。使用@Inherited可以让这些配置自动应用到所有子类,减少重复配置。
注意事项
不改变运行时行为
:@Inherited仅影响编译时或反射时对类是否有特定注解的判断,并不直接影响程序的运行时行为。成员注解不适用
:@Inherited仅对类级别的注解有效,方法、字段等成员的注解即使父类有,子类也不会自动继承。反射获取
:即使注解是继承的,使用反射API(如Class.getAnnotation())直接在子类上获取注解时,如果没有在子类上显式声明,可能会得到null。这时可以使用Class.getDeclaredAnnotation()或框架提供的工具方法(如Spring的AnnotatedElementUtils.findMergedAnnotation())来查找包括继承在内的注解。
由上面可知,有无@Inherited会影响到类级别的注解是否有效,下面按照有无@Inherited注解进行分析
实体类
@MyAnnotation(value = "Class")
@Slf4j
public class Animal {
@MyAnnotation(value = "Method")
public void foo() {
}
@MyAnnotation(value = "Method")
public void bar() {
}
}
@Slf4j
public class Pig extends Animal {
@Override
public void foo() {
}
}
自定义注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited // 有无,决定着类级别注解可以继承
public @interface MyAnnotation {
String value();
}
测试方法
@Slf4j
public class Main {
public static void main(String[] args) throws NoSuchMethodException {
wrong();
right();
}
/**
* 获取 MyAnnotation 的 value 属性值,如果注解不存在则返回空字符串。
* @param annotation
* @return
*/
private static String getAnnotationValue(MyAnnotation annotation) {
if (annotation == null) return "";
return annotation.value();
}
public static void wrong() throws NoSuchMethodException {
Animal animal = new Animal();
log.info("AnimalClass:{}", getAnnotationValue(animal.getClass().getAnnotation(MyAnnotation.class)));
log.info("AnimalMethod:{}", getAnnotationValue(animal.getClass().getMethod("foo").getAnnotation(MyAnnotation.class)));
Pig pig = new Pig();
log.info("PigClass:{}", getAnnotationValue(pig.getClass().getAnnotation(MyAnnotation.class)));
System.out.println("重写父类的方法");
log.info("PigMethod1:{}", getAnnotationValue(pig.getClass().getMethod("foo").getAnnotation(MyAnnotation.class)));
System.out.println("继承父类的方法");
log.info("PigMethod2:{}", getAnnotationValue(pig.getClass().getMethod("bar").getAnnotation(MyAnnotation.class)));
}
public static void right() throws NoSuchMethodException {
Animal animal = new Animal();
log.info("AnimalClass:{}", getAnnotationValue(animal.getClass().getAnnotation(MyAnnotation.class)));
log.info("AnimalMethod:{}", getAnnotationValue(animal.getClass().getMethod("foo").getAnnotation(MyAnnotation.class)));
Pig pig = new Pig();
log.info("PigClass:{}", getAnnotationValue(AnnotatedElementUtils.findMergedAnnotation(pig.getClass(), MyAnnotation.class)));
System.out.println("重写父类的方法");
log.info("PigMethod1:{}", getAnnotationValue(AnnotatedElementUtils.findMergedAnnotation(pig.getClass().getMethod("foo"), MyAnnotation.class)));
System.out.println("继承父类的方法");
log.info("PigMethod2:{}", getAnnotationValue(AnnotatedElementUtils.findMergedAnnotation(pig.getClass().getMethod("foo"), MyAnnotation.class)));
}
}
运行结果
使用@Inherited
17:08:16.895 [main] INFO com.kdz.annotationinheritance.Main - AnimalClass:Class //肯定可以看到
17:08:16.897 [main] INFO com.kdz.annotationinheritance.Main - AnimalMethod:Method //肯定可以看到
17:08:16.897 [main] INFO com.kdz.annotationinheritance.Main - PigClass:Class //由于使用了@Inherited注解,可以显示地看到注解
重写父类的方法
17:08:16.897 [main] INFO com.kdz.annotationinheritance.Main - PigMethod1: //子类重写了该方法而没有保留注解,直接通过反射获取时找不到注解
继承父类的方法
17:08:16.898 [main] INFO com.kdz.annotationinheritance.Main - PigMethod2:Method //子类继承了该方法,直接通过反射获取时找到注解
17:08:16.898 [main] INFO com.kdz.annotationinheritance.Main - AnimalClass:Class
17:08:16.898 [main] INFO com.kdz.annotationinheritance.Main - AnimalMethod:Method
17:08:16.952 [main] INFO com.kdz.annotationinheritance.Main - PigClass:Class
重写父类的方法
17:08:16.952 [main] INFO com.kdz.annotationinheritance.Main - PigMethod1:Method
继承父类的方法
17:08:16.952 [main] INFO com.kdz.annotationinheritance.Main - PigMethod2:Method
不使用@Inherited
注释掉@Inherited
17:11:47.786 [main] INFO com.kdz.annotationinheritance.Main - AnimalClass:Class //肯定可以看到
17:11:47.788 [main] INFO com.kdz.annotationinheritance.Main - AnimalMethod:Method //肯定可以看到
17:11:47.788 [main] INFO com.kdz.annotationinheritance.Main - PigClass:
重写父类的方法
17:11:47.788 [main] INFO com.kdz.annotationinheritance.Main - PigMethod1: //由于没有使用@Inherited注解,所以无法看到
继承父类的方法
17:11:47.788 [main] INFO com.kdz.annotationinheritance.Main - PigMethod2:Method //子类继承了该方法,直接通过反射获取时找到注解17:11:47.788 [main] INFO com.kdz.annotationinheritance.Main - AnimalClass:Class
17:11:47.788 [main] INFO com.kdz.annotationinheritance.Main - AnimalMethod:Method
17:11:47.839 [main] INFO com.kdz.annotationinheritance.Main - PigClass:Class
重写父类的方法
17:11:47.840 [main] INFO com.kdz.annotationinheritance.Main - PigMethod1:Method
继承父类的方法
17:11:47.840 [main] INFO com.kdz.annotationinheritance.Main - PigMethod2:Method
结论
自定义注解上有@Inherited | 自定义注解上无@Inherited | |
---|---|---|
子类的类上能否继承到父类的类上的注解? | √ | × |
子类方法,重写了父类上的方法,这个方法能否继承到注解? | × | × |
子类方法,继承了父类上的方法,这个方法能否继承到注解? | √ | √ |
使用特定工具方法(如Spring的AnnotatedElementUtils.findMergedAnnotation())来合并注解信息,父类的类级别、方法注解对子类都是可见