Inherited Method Annotations

  Subscribe
11/2/2006 - Marco (updated on 11/13/2017)

See Finding Conforming Methods for part one of this two-part article.

The problem we're working on is as follows:

  1. Given an object, a method name and a list of parameters, execute the matching method on the given object.
  2. Determine from the object's class whether the given method can be executed from the given context (web, command-line, etc.)

We will use annotations to mark up methods as callable or not. Given the Method we obtained in part one, it shouldn't be too hard to find its annotations. Simply pass the class of the desired annotation to getAnnotation(); if the annotation was specified for that method, we check its contents to determine whether the method can be called or not.

These are not the Annotations you're Looking For

In part one, calling getConformingMethod( "giveCommandTo", {new Assistant()}, Manager.getClass()) returns the overridden method from the Manager class. Unfortunately, a call to getAnnotations() on this method returns an empty list. Why?

The Java reflection API makes a distinction between annotations that appear directly on an element and all annotations for an element, including ancestors. These two lists can be retrieved from any AnnotatedElement using the following methods:

Annotation[] getAnnotations();
  Annotation[] getDeclaredAnnotations();

The documentation states that getDeclaredAnnotations() returns "all annotations that are directly present on this element", whereas getAnnotations() returns "all annotations present on this element". The key word here is directly, which is to be interpreted as stated above ... for classes. For methods, there is no notion of inheritance per se in the reflection API. That is, if a method in a base class has an annotation and that method is overridden in a descendent, the signature for the method in the descendent returns empty lists for both getDeclaredAnnotations() and getAnnotations().

This doesn't make any sense and directly contradicts the documentation. It seems that the all vs. declared distinction only holds for classes, even though it is defined for all elements. A quick look into the Java source shows that Method inherits from AccessibleObject, which implements the AnnotatedElement interface. AccessibleObject implements getAnnotations() with the following code:

public Annotation[] getAnnotations() { 
        return getDeclaredAnnotations();
    }

Alrighty then! Method itself does not override this method, so it's relatively clear that inherited annotations are not available from a method. In effect, the @inherited keyword only has an effect for classes, which is a shame. A quick check of the documentation for that keyword verifies this claim:

Note that this meta-annotation type has no effect if the annotated type is used to annotate anything other than a class.

So, once again, we're on our own and must build the functionality in a custom function. The code below shows how to search a method and its inherited implementations for the Callable interface:

private Callable getCallable(Method m, Object[] actualParameters) {
    result = null;
    if (m != null) {
      Callable result = m.getAnnotation(Callable.class);
      if (result == null) {
        Class<?> parent = m.getDeclaringClass().getSuperclass();
        if (parent != null) {
          Method superMethod = getConformingMethod(m.getName(), actualParameters, parent);
          result = getCallable(superMethod, actualParameters);
        }
      }
    }
    return result;
  }

It's not rocket science, but it involves a lot of digging around in the guts of Java reflection that shouldn't be necessary.


Using Java 1.5

Sign up for our Newsletter