14

I have an interface with an annotated method. The annotation is marked with @Inherited, so I expect an implementor to inherit it. However, it is not the case:

Code:

import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
import java.util.Arrays;

public class Example {

    public static void main(String[] args) throws SecurityException, NoSuchMethodException {
        TestInterface obj = new TestInterface() {
            @Override
            public void m() {}
        };

        printMethodAnnotations(TestInterface.class.getMethod("m"));
        printMethodAnnotations(obj.getClass().getMethod("m"));
    }

    private static void printMethodAnnotations(Method m) {
        System.out.println(m + ": " + Arrays.toString(m.getAnnotations()));
    }
}

interface TestInterface {
    @TestAnnotation
    public void m();
}

@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface TestAnnotation {}

The above code prints:

public abstract void annotations.TestInterface.m(): [@annotations.TestAnnotation()]

public void annotations.Example$1.m(): []

So the question is why does not the obj.m() have @TestAnnotation despite that it implements a method marked with @TestAnnotation which is @Inherited?

Community
  • 1
  • 1
vitaly
  • 2,755
  • 4
  • 23
  • 35
  • 1
    The answer was right there in the documentation of `@Inherited` as pointed out by @AbhinavSakar and @Gilberto. Sorry about that. I did quite a bit of experimentation but missed the obvious - the docs. – vitaly Sep 14 '12 at 17:08
  • By the way, any idea on what is the reason for such a design decision? I would expect the meta-annotation to work on methods too. – vitaly Sep 14 '12 at 17:09
  • No problem, it is surprising that super-classes are treated differently from super-interfaces. That's why some frameworks (such as Spring) ship with reflection utilities that also query the super interfaces. – meriton Sep 14 '12 at 17:10
  • Just out of curiosity, which utility in Spring is that? – vitaly Sep 14 '12 at 17:14
  • [`org.springframework.core.type.classreading.MetadataReader`](http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/core/type/classreading/MetadataReader.html). – meriton Sep 14 '12 at 17:25
  • possible duplicate of [Why java classes do not inherit annotations from implemented interfaces?](http://stackoverflow.com/questions/4745798/why-java-classes-do-not-inherit-annotations-from-implemented-interfaces) – Steve Chambers Mar 04 '14 at 10:23

3 Answers3

33

From the javadocs of java.lang.annotation.Inherited:

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.

Abhinav Sarkar
  • 23,534
  • 11
  • 81
  • 97
23

From the @Inherited javadoc:

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.`

In summary, it doesn't apply to methods.

Vikdor
  • 23,934
  • 10
  • 61
  • 84
Gilberto Torrezan
  • 5,113
  • 4
  • 31
  • 50
3

Alternatively, you can use reflection to derive the same information. The method printMethodAnnotations can be rewritten as:

private static void printMethodAnnotations(Method m) {
    Class<?> methodDeclaredKlass = m.getDeclaringClass();
    List<Class<?>> interfases = org.apache.commons.lang3.ClassUtils.getAllInterfaces(methodDeclaredKlass);
    List<Annotation> annotations = new ArrayList<>();
    annotations.addAll(Arrays.asList(m.getAnnotations()));
    for (Class<?> interfase : interfases) {
        for (Method interfaseMethod : interfase.getMethods()) {
            if (areMethodsEqual(interfaseMethod, m)) {
                annotations.addAll(Arrays.asList(interfaseMethod.getAnnotations()));
                continue;
            }
        }
    }
    System.out.println(m + "*: " + annotations);
}

private static boolean areMethodsEqual(Method m1, Method m2) {
    // return type, Modifiers are not required to check, if they are not appropriate match then it will be a compile
    // time error. This needs enhancements for Generic types parameter ?
    return m1.getName().equals(m2.getName()) && Arrays.equals(m1.getParameterTypes(), m2.getParameterTypes());
}
Clijsters
  • 4,031
  • 1
  • 27
  • 37
Sridivakar
  • 163
  • 10