Reputation: 8105
I have a public API like:
interface Alien {
@Help( "Make him say something" )
String speak();
}
and an implementation like:
class Dalek implements Alien {
@Override
String speak(){ return "Exterminate!"; }
}
Now I need to access the Annotation @Help
on the base interface but I only have the implementation / derived class which may implements other interfaces or extend other classes, too.
So is there a sane way in Java to find the "base" method which is overridden or another way to get to that annotation?
Pseudocode for what I'm looking for would be like:
Method m;
m.getDeclaringMethod().getAnnotation( Help.class );
// or:
m.getAnnotationRecusively( Help.class )
Upvotes: 4
Views: 2986
Reputation: 298103
No, there is no “sane way” to do this regarding interfaces. The problem is that there is no unique “base” method. A method declaration may override a super class method and implement multiple unrelated interface methods at the same time, e.g.
class A {
public void foo() {}
}
interface I1 {
void foo();
}
interface I2 {
void foo();
}
class B extends A implements I1, I2 {
@Override
public void foo() {}
}
Then, when you use this naive approach:
public static void printCandidates(Method m) {
Class<?> cl=m.getDeclaringClass();
String name=m.getName(); Class<?>[] param=m.getParameterTypes();
for(Class<?> i: cl.getInterfaces()) try {
Method ifM=i.getMethod(name, param);
System.out.println("candidate "+ifM.getDeclaringClass().getName()+'.'+name);
} catch(NoSuchMethodException ex) { ex.printStackTrace();}
for(;;) {
System.out.println("candidate "+cl.getName()+'.'+name);
cl=cl.getSuperclass();
if(cl==null) break;
try { m=cl.getMethod(name, param); }
catch(NoSuchMethodException ex) { break; }
}
}
and invoke printCandidates(new B().getClass().getMethod("foo"));
, it will print
candidate I1.foo
candidate I2.foo
candidate B.foo
candidate A.foo
but note that interfaces support multiple inheritance and also redundant implements
specifications, e.g. you may change above scenario as
interface I3 extends I2, I1 {
}
interface I4 extends I2 {
void foo();
}
class B extends A implements I3, I4, I2 {
@Override
public void foo() {}
}
and we will get
candidate I2.foo
candidate I4.foo
candidate I2.foo
candidate B.foo
candidate A.foo
Note, how the declaration I2.foo
appears twice, but when I4.foo
has an annotation, it should have precedence as it overrides I2.foo
, whereas I1.foo
doesn’t appear but needs to be considered and is not overridden. The entire logic for supporting multiple inheritance, potentially redundantly specified interfaces and some interfaces containing overriding specifications is quite complex. But it gets worse:
class B extends A implements I3, I4, I2 {
}
After making this change, we get:
candidate A.foo
and nothing else. B
is implementing all the interfaces using the method inherited from A
, so when looking up the method foo
in B
we get a Method
object reporting A
as its declaring class, which doesn’t implement any interface. So a Method
object is not capable of carrying the required type of information for your intended lookup. The annotation search method needs more information than that, if it ought to find all interface method candidates, e.g. you may need to pass the actual Class
explicitly.
Note that ordinary Java operations don’t need to deal with such a complexity. You may invoke an interface method on an actual object and regardless of how complex the interface inheritance hierarchy is, the implementation method is searched on the concrete class of the object, traversing the linear superclass hierarchy when necessary (the new default
methods change this a bit, but it’s still simpler as your task, as overridden methods still are irrelevant for invocation).
An implementation for your kind of task is not provided by the JRE.
Upvotes: 4