Scheintod
Scheintod

Reputation: 8105

Java reflection: Find base class/interface a method was declared in

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

Answers (1)

Holger
Holger

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

Related Questions