Java反射:查找声明方法的基类/接口

时间:2016-01-20 16:55:48

标签: java reflection annotations

我有一个公共API,如:

interface Alien {
    @Help( "Make him say something" )
    String speak();
}

和类似的实现:

class Dalek implements Alien {
    @Override
    String speak(){ return "Exterminate!"; }
}

现在我需要访问基接口上的注释@Help,但我只有实现/派生类,它可以实现其他接口或扩展其他类。

在Java中找到" base"是否有一种理智的方式被覆盖的方法或其他获取该注释的方法?

我正在寻找的伪代码就像:

Method m;

m.getDeclaringMethod().getAnnotation( Help.class );

// or:

m.getAnnotationRecusively( Help.class )

1 个答案:

答案 0 :(得分:2)

不,关于接口没有“理智的方式”。问题是没有独特的“基础”方法。方法声明可以覆盖超类方法并同时实现多个不相关的接口方法,例如

class A {
    public void foo() {}
}
interface I1 {
    void foo();
}
interface I2 {
    void foo();
}
class B extends A implements I1, I2 {
    @Override
    public void foo() {}
}

然后,当你使用这种天真的方法时:

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; }
    }
}

并调用printCandidates(new B().getClass().getMethod("foo"));,它将打印

candidate I1.foo
candidate I2.foo
candidate B.foo
candidate A.foo

但请注意,接口支持多重继承以及冗余implements规范,例如您可以将上述方案更改为

interface I3 extends I2, I1 {
}
interface I4 extends I2 {
    void foo();
}
class B extends A implements I3, I4, I2 {
    @Override
    public void foo() {}
}

我们会得到

candidate I2.foo
candidate I4.foo
candidate I2.foo
candidate B.foo
candidate A.foo

注意,声明I2.foo如何出现两次,但I4.foo有注释时,它应该优先,因为它会覆盖I2.foo,而I1.foo则不会出现但需要考虑并且不会被覆盖。支持多重继承的整个逻辑,可能是冗余指定的接口和一些包含重写规范的接口非常复杂。但它变得更糟:

class B extends A implements I3, I4, I2 {
}

进行此更改后,我们得到:

candidate A.foo

,没有别的。 B正在使用从A继承的方法实现所有接口,因此在foo中查找方法B时,我们会得到一个Method对象报告{{ 1}}作为其声明类,它不实现任何接口。因此,A对象无法为您的预期查找提供所需类型的信息。注释搜索方法需要更多信息,如果它应该找到所有接口方法候选者,例如您可能需要明确传递实际的Method

请注意,普通的Java操作不需要处理这种复杂性。您可以在实际对象上调用接口方法,无论接口继承层次结构有多复杂,都会在对象的具体类上搜索实现方法,在必要时遍历线性超类层次结构(新的Class方法稍微改变一下,但它仍然比你的任务更简单,因为重写的方法仍然与调用无关。)

JRE不提供针对您的任务的实现。