This page shows the source for this entry, with WebCore formatting language tags and attributes highlighted.
Title
Inherited Method Annotations
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=1423">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>