80

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.

Ahmed Ghoneim
  • 6,834
  • 9
  • 49
  • 79
Travis Webb
  • 14,688
  • 7
  • 55
  • 109

4 Answers4

96

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.

Matthew Read
  • 1,365
  • 1
  • 30
  • 50
trutheality
  • 23,114
  • 6
  • 54
  • 68
  • 40
    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. – Tustin2121 Mar 26 '13 at 15:10
  • 1
    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? – John B Aug 21 '14 at 13:53
  • 1
    if I have `Super abc = new Sub();` will method `foo` of object `abc` be marked with `@MyAnnotation("hello")`? – Oleg Vazhnev Dec 07 '18 at 13:47
12

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);
}
Saintali
  • 4,482
  • 2
  • 29
  • 49
  • 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... – Uri Agassi Jun 16 '15 at 10:36
  • @UriAgassi That's by design. I think the JavaDoc makes it clear. – Saintali Jun 19 '15 at 20:51
  • 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) – Uri Agassi Jun 20 '15 at 06:28
9

Using Spring Core you can resolve with

AnnotationUtils.java

jrey
  • 2,163
  • 1
  • 32
  • 48
2

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;
    }
}
Trevor Robinson
  • 15,694
  • 5
  • 73
  • 72