How do Java method annotations work in conjunction with method overriding?

37,458

Solution 1

Copied verbatim from http://www.eclipse.org/aspectj/doc/released/adk15notebook/annotations.html#annotation-inheritance:

Annotation Inheritance

It is important to understand the rules relating to inheritance of annotations, as these have a bearing on join point matching based on the presence or absence of annotations.

By default annotations are not inherited. Given the following program

        @MyAnnotation
        class Super {
          @Oneway public void foo() {}
        }

        class Sub extends Super {
          public void foo() {}
        }

Then Sub does not have the MyAnnotation annotation, and Sub.foo() is not an @Oneway method, despite the fact that it overrides Super.foo() which is.

If an annotation type has the meta-annotation @Inherited then an annotation of that type on a class will cause the annotation to be inherited by sub-classes. So, in the example above, if the MyAnnotation type had the @Inherited attribute, then Sub would have the MyAnnotation annotation.

@Inherited annotations are not inherited when used to annotate anything other than a type. A type that implements one or more interfaces never inherits any annotations from the interfaces it implements.

Solution 2

You found your answer already: there is no provision for method-annotation inheritance in the JDK.

But climbing the super-class chain in search of annotated methods is also easy to implement:

/**
 * Climbs the super-class chain to find the first method with the given signature which is
 * annotated with the given annotation.
 *
 * @return A method of the requested signature, applicable to all instances of the given
 *         class, and annotated with the required annotation
 * @throws NoSuchMethodException If no method was found that matches this description
 */
public Method getAnnotatedMethod(Class<? extends Annotation> annotation,
                                 Class c, String methodName, Class... parameterTypes)
        throws NoSuchMethodException {

    Method method = c.getMethod(methodName, parameterTypes);
    if (method.isAnnotationPresent(annotation)) {
        return method;
    }

    return getAnnotatedMethod(annotation, c.getSuperclass(), methodName, parameterTypes);
}

Solution 3

Using Spring Core you can resolve with

AnnotationUtils.java

Solution 4

While the answer to the question as asked is that Java's Method.getAnnotation() does not consider overridden methods, sometimes it is useful to find these annotations. Here is a more complete version of Saintali's answer that I'm currently using:

public static <A extends Annotation> A getInheritedAnnotation(
    Class<A> annotationClass, AnnotatedElement element)
{
    A annotation = element.getAnnotation(annotationClass);
    if (annotation == null && element instanceof Method)
        annotation = getOverriddenAnnotation(annotationClass, (Method) element);
    return annotation;
}

private static <A extends Annotation> A getOverriddenAnnotation(
    Class<A> annotationClass, Method method)
{
    final Class<?> methodClass = method.getDeclaringClass();
    final String name = method.getName();
    final Class<?>[] params = method.getParameterTypes();

    // prioritize all superclasses over all interfaces
    final Class<?> superclass = methodClass.getSuperclass();
    if (superclass != null)
    {
        final A annotation =
            getOverriddenAnnotationFrom(annotationClass, superclass, name, params);
        if (annotation != null)
            return annotation;
    }

    // depth-first search over interface hierarchy
    for (final Class<?> intf : methodClass.getInterfaces())
    {
        final A annotation =
            getOverriddenAnnotationFrom(annotationClass, intf, name, params);
        if (annotation != null)
            return annotation;
    }

    return null;
}

private static <A extends Annotation> A getOverriddenAnnotationFrom(
    Class<A> annotationClass, Class<?> searchClass, String name, Class<?>[] params)
{
    try
    {
        final Method method = searchClass.getMethod(name, params);
        final A annotation = method.getAnnotation(annotationClass);
        if (annotation != null)
            return annotation;
        return getOverriddenAnnotation(annotationClass, method);
    }
    catch (final NoSuchMethodException e)
    {
        return null;
    }
}
Share:
37,458
Travis Webb
Author by

Travis Webb

Updated on July 05, 2022

Comments

  • Travis Webb
    Travis Webb almost 2 years

    I have a parent class Parent and a child class Child, defined thus:

    class Parent {
        @MyAnnotation("hello")
        void foo() {
            // implementation irrelevant
        }
    }
    class Child extends Parent {
        @Override
        foo() {
            // implementation irrelevant
        }
    }
    

    If I obtain a Method reference to Child::foo, will childFoo.getAnnotation(MyAnnotation.class) give me @MyAnnotation? Or will it be null?

    I'm interested more generally in how or whether annotation works with Java inheritance.

  • Tustin2121
    Tustin2121 over 11 years
    Also, trutheality, I searched before I asked, and I came up with this page. Congrats, you are now part of those search results. That's why this website is here. :) Also, your answer is much more concise than looking through that doc.
  • John B
    John B almost 10 years
    One question to further this... if a framework finds the method based on the annotation and then invokes it, which version of the method is invoked? The child class' method should override the parent but is that respected with reflective invocation?
  • Uri Agassi
    Uri Agassi about 9 years
    This does not provision for a situation where the requested annotation is not found in any of the superclass implementations - in this case it will throw NoSuchMethodException, although null would be a legitimate return value...
  • Saintali
    Saintali about 9 years
    @UriAgassi That's by design. I think the JavaDoc makes it clear.
  • Uri Agassi
    Uri Agassi about 9 years
    the only thing the javadoc makes clear is that a NoSuchMethodException is thrown. It should throw such an exception when the method does not exist, not when the annotation is not found... it is not compatible with the original implementation (which will return null in such a case)
  • Oleg Vazhnev
    Oleg Vazhnev over 5 years
    if I have Super abc = new Sub(); will method foo of object abc be marked with @MyAnnotation("hello")?