java注解继承(来自源码的解答)

事情的经过是,某天突然对注解继承有点兴趣,于是面向搜索引擎准备解惑下,发现网上都是自己写的例子然后给出结论,而且例子也没有完全覆盖一些情况。于是翻找Java源码得出了以下的输出。
Inherited 类是元注解类,只能修饰注解。

英文说明的第一大段大致意思是:会沿着子类一直找到Object类,把标记为@Inherited的注解信息全部拿到。简单来说标记为@Inherited注解的父类,注解信息就会继承到子类。

第二大段是说:该@Inherited只有标记类的时候才有效果。而且该注解只对父类上的注解有效果,对父接口上的注解是没有效果滴。

/**
 * Indicates that an annotation type is automatically inherited.  If
 * an Inherited meta-annotation is present on an annotation type
 * declaration, and the user queries the annotation type on a class
 * declaration, and the class declaration has no annotation for this type,
 * then the class's superclass will automatically be queried for the
 * annotation type.  This process will be repeated until an annotation for this
 * type is found, or the top of the class hierarchy (Object)
 * is reached.  If no superclass has an annotation for this type, then
 * the query will indicate that the class in question has no such annotation.
 *
 * <p>Note that this meta-annotation type has no effect if the annotated
 * type is used to annotate anything other than a class.  Note also
 * that this meta-annotation only causes annotations to be inherited
 * from superclasses; annotations on implemented interfaces have no
 * effect.
 *
 * @author  Joshua Bloch
 * @since 1.5
 * @jls 9.6.3.3 @Inherited
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

如果,同学只想要一个结果,那么看到这里就足够了。因为接下来要分析为什么会这样。

准备工作需要自定义一个注解:

import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * 自定义注解,参考{@link javax.annotation.Resource}
 *
 * @author <a href="wupeng6684@163.com">pengwu</a>
 * @since
 **/
@Target({TYPE, FIELD, METHOD})
@Retention(RUNTIME)
//@Inherited // 开启之后只是影响 if (AnnotationType.getInstance(annotationClass).isInherited()) 判断而已
public @interface MyAnnotation {

    String name() default "";

}

为了覆盖所有情况,准备一个接口、一个抽象类、一个普通类

@MyAnnotation(name = "myInterface")
public interface MyInterface {
    
    @MyAnnotation(name = "myInterface-overrideMethod")
    void overrideMethod();
    
    @MyAnnotation(name = "myInterface-method")
    default void method() {
        
    }
}
@MyAnnotation(name = "myAbstractClazz")
public abstract class MyAbstractClazz {
    
    @MyAnnotation(name = "myAbstractClazz-method")
    public void method() {
        
    }

    @MyAnnotation(name = "myAbstractClazz-overrideMethod")
    public abstract void overrideMethod();
}
@MyAnnotation(name = "myNormalClazz")
public class MyNormalClazz {
    
    @MyAnnotation(name = "myNormalClazz-method")
    public void method() {
        
    }
    
    @MyAnnotation(name ="myNormalClazz-overrideMethod")
    public void overrideMethod() {
        
    }
}

最后编写一个测试类:

public class MyAnnotationDemo {

    public static class MyInterfaceImpl implements MyInterface {
        @Override
        public void overrideMethod() {
            
        }
    }
    
    public static class MyNormalClazzImpl extends MyNormalClazz {
        @Override
        public void overrideMethod() {
            super.overrideMethod();
        }
    }
    
    public static class MyAbstractClazzImpl extends MyAbstractClazz {
        @Override
        public void overrideMethod() {
            
        }
    }

    public static void main(String[] args) throws NoSuchMethodException {
        showAnnotation(MyInterfaceImpl.class);
//        showAnnotation(MyInterfaceImpl.class.getInterfaces()[0]);//getAnnotations() 不取父类的接口信息,只能自己手动获取了
        showAnnotation(MyNormalClazzImpl.class);
//        showAnnotation(MyNormalClazzImpl.class.getSuperclass());//getAnnotations() 因为注解不让继承,我只好将父类取出去获取
        showAnnotation(MyAbstractClazzImpl.class);

        // 虽然源码都说了,只对superclasses有效,但我还是想试试
        // 以下方法都是未被override的方法
        Method method = MyInterfaceImpl.class.getMethod("method");
        showAnnotation(method);
        Method method1 = MyNormalClazzImpl.class.getMethod("method");
        showAnnotation(method1);
        Method method2 = MyAbstractClazzImpl.class.getMethod("method");
        showAnnotation(method2);
        
        // 以下方法都是被override的方法
        Method overrideMethod = MyInterfaceImpl.class.getMethod("overrideMethod");
        showAnnotation(overrideMethod);
        Method overrideMethod1 = MyNormalClazzImpl.class.getMethod("overrideMethod");
        showAnnotation(overrideMethod1);
        Method overrideMethod2 = MyAbstractClazzImpl.class.getMethod("overrideMethod");
        showAnnotation(overrideMethod2);
    }
    
    private static void showAnnotation(AnnotatedElement clazz) {
        Annotation[] annotations = clazz.getAnnotations();
        Stream.of(annotations).forEach(annotation -> {
            System.out.println(annotation);
        });
    }
    
}

@MyAnnotation 不添加@Inherited 时输出结果:

@com.wp.beans.MyAnnotation(name=myInterface-method)
@com.wp.beans.MyAnnotation(name=myNormalClazz-method)
@com.wp.beans.MyAnnotation(name=myAbstractClazz-method)

子类没有继承父类的注解,被覆写的方法没有继承父类方法的注解,未被覆写的方法获取到了注解。
@MyAnnotation 添加@Inherited 时输出结果:

@com.wp.beans.MyAnnotation(name=myNormalClazz)
@com.wp.beans.MyAnnotation(name=myAbstractClazz)
@com.wp.beans.MyAnnotation(name=myInterface-method)
@com.wp.beans.MyAnnotation(name=myNormalClazz-method)
@com.wp.beans.MyAnnotation(name=myAbstractClazz-method)

接口没有继承父类的注解,抽象类和类继承了父类的注解,方法没有任何变化。

结果符合源码注释里的描述,很遗憾这次的注释没有问题。

那么为什么@Inherited能够实现class的注解继承,Interface却不行呢。
通过getAnnotations() 调用链找到的起决定作用的核心方法如下,两个重点:
getSuperclass();//不包括父类接口
该判断通过源码调试发现,其实上面父类的注解都取到了,isInherited()为false不进行记录。
if (AnnotationType.getInstance(annotationClass).isInherited())

private AnnotationData createAnnotationData(int classRedefinedCount) {
        Map<Class<? extends Annotation>, Annotation> declaredAnnotations =
            AnnotationParser.parseAnnotations(getRawAnnotations(), getConstantPool(), this);
        Class<?> superClass = getSuperclass();//不包括父类接口
        Map<Class<? extends Annotation>, Annotation> annotations = null;
        if (superClass != null) {
            Map<Class<? extends Annotation>, Annotation> superAnnotations =
                superClass.annotationData().annotations;
            for (Map.Entry<Class<? extends Annotation>, Annotation> e : superAnnotations.entrySet()) {
                Class<? extends Annotation> annotationClass = e.getKey();
                if (AnnotationType.getInstance(annotationClass).isInherited()) {//其实父类class的注解我取到了,但是不让继承只好跳过
                    if (annotations == null) { // lazy construction
                        annotations = new LinkedHashMap<>((Math.max(
                                declaredAnnotations.size(),
                                Math.min(12, declaredAnnotations.size() + superAnnotations.size())
                            ) * 4 + 2) / 3
                        );
                    }
                    annotations.put(annotationClass, e.getValue());
                }
            }
        }
        if (annotations == null) {
            // no inherited annotations -> share the Map with declaredAnnotations
            annotations = declaredAnnotations;
        } else {
            // at least one inherited annotation -> declared may override inherited
            annotations.putAll(declaredAnnotations);
        }
        return new AnnotationData(annotations, declaredAnnotations, classRedefinedCount);
    }

那假如非得取父类接口的注解怎么办,getAnnotations()方法是不行了,但可以曲线救国:
该方式可以正常获取到接口上的注解信息,可以基于该思路搞个工具类专门处理接口上的注解问题。

showAnnotation(MyInterfaceImpl.class.getInterfaces()[0]);//getAnnotations() 不取父类的接口信息,只能自己手动获取了

父类上的注解没添加@Inherited,子类还是想取到怎么办,可以如下处理:

showAnnotation(MyNormalClazzImpl.class.getSuperclass());//getAnnotations() 因为注解不让继承,我只好将父类取出去获取

最后就是,为什么方法上不能继承:
Method获取注解的getAnnotations()方法,核心逻辑如下:
比较有迷惑性的是getRoot()以为要返回父类的方法,实际调试一直返回的都是自己。
这个getRoot()不知道干啥使得,有知道的小伙伴可以写在评论区,帮助解解惑。

private synchronized  Map<Class<? extends Annotation>, Annotation> declaredAnnotations() {
        if (declaredAnnotations == null) {
            Executable root = getRoot();//返回的都当前方法
            if (root != null) {
                declaredAnnotations = root.declaredAnnotations();
            } else {
                declaredAnnotations = AnnotationParser.parseAnnotations(
                    getAnnotationBytes(),
                    sun.misc.SharedSecrets.getJavaLangAccess().
                    getConstantPool(getDeclaringClass()),
                    getDeclaringClass());
            }
        }
        return declaredAnnotations;
    }

对比下来觉得Class和Method的结构决定了两者能支持的不同特性,比如Method 没有getSuperMethod()类似的方法无法支持对应的特性。

以上就是我基于源码分析,示例演示的全过程,如有问题可及时留下宝贵的评论。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值