This page shows the source for this entry, with WebCore formatting language tags and attributes highlighted.

Title

Inherited Method Annotations in Java

Description

<n>This article was originally published on the <a href="http://blogs.encodo.ch/news/view_article.php?id=6"><b>Encodo Blogs</b></a>. Browse on over to see more!</n> <hr> <n>See <a href="{app}view_article.php?id=1410">Finding Conforming Methods</a> for part one of this two-part article.</n> The problem we're working on is as follows: <ol> Given an object, a method name and a list of parameters, execute the matching method on the given object. Determine from the object's class whether the given method can be executed from the given context (web, command-line, etc.) </ol> We will use annotations to mark up methods as <i>callable</i> or not. Given the <c>Method</c> we obtained in part one, it shouldn't be too hard to find its annotations. Simply pass the class of the desired annotation to <c>getAnnotation()</c>; if the annotation was specified for that method, we check its contents to determine whether the method can be called or not. <h>These are not the Annotations you're Looking For</h> In part one, calling <c>getConformingMethod( "giveCommandTo", {new Assistant()}, Manager.getClass())</c> returns the overridden method from the <c>Manager</c> class. Unfortunately, a call to <c>getAnnotations()</c> on this method returns an empty list. Why? The Java reflection API makes a distinction between annotations that appear directly on an element and <i>all</i> annotations for an element, including ancestors. These two lists can be retrieved from any <c>AnnotatedElement</c> using the following methods: <code> Annotation[] getAnnotations(); Annotation[] getDeclaredAnnotations(); </code> The documentation states that <c>getDeclaredAnnotations()</c> returns <iq>all annotations that are directly present on this element</iq>, whereas <c>getAnnotations()</c> returns <iq>all annotations present on this element</iq>. The key word here is <i>directly</i>, which is to be interpreted as stated above ... for <i>classes</i>. 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 <c>getDeclaredAnnotations()</c> and <c>getAnnotations()</c>. 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 <c>Method</c> inherits from <c>AccessibleObject</c>, which implements the <c>AnnotatedElement</c> interface. <c>AccessibleObject</c> implements <c>getAnnotations()</c> with the following code: <code> public Annotation[] getAnnotations() { return getDeclaredAnnotations(); } </code> Alrighty then! <c>Method</c> itself does not override this method, so it's relatively clear that inherited annotations are not available from a method. In effect, the <c>@inherited</c> keyword only has an effect for classes, which is a shame. A quick check of the documentation for <i>that</i> keyword verifies this claim: <bq>Note that this meta-annotation type has no effect if the annotated type is used to annotate anything other than a class.</bq> 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 <c>Callable</c> interface: <code> 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; } </code> It's not rocket science, but it involves a lot of digging around in the guts of Java reflection that shouldn't be necessary. <hr> <n>Using Java 1.5</n>