事情的经过是,某天突然对注解继承有点兴趣,于是面向搜索引擎准备解惑下,发现网上都是自己写的例子然后给出结论,而且例子也没有完全覆盖一些情况。于是翻找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()类似的方法无法支持对应的特性。
以上就是我基于源码分析,示例演示的全过程,如有问题可及时留下宝贵的评论。