【问题标题】:Java reflection: Find base class/interface a method was declared inJava反射:查找在其中声明方法的基类/接口
【发布时间】:2016-01-20 16:55:48
【问题描述】:

我有一个公共 API,例如:

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

以及类似的实现:

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

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

那么在 Java 中是否有一种理智的方法可以找到被覆盖的“基本”方法或其他方法来获取该注释?

我正在寻找的伪代码如下:

Method m;

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

// or:

m.getAnnotationRecusively( Help.class )

【问题讨论】:

  • 没有。它不是。这些问题要求解释注释是如何工作的。答案解释了注释不是继承的。我的问题是如何找到它们(或者更一般地覆盖方法)。

标签: java reflection annotations


【解决方案1】:

不,在接口方面没有“理智的方法”可以做到这一点。问题是没有唯一的“基础”方法。一个方法声明可以覆盖一个超类方法,同时实现多个不相关的接口方法,例如

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 继承的方法实现所有接口,因此当在 B 中查找方法 foo 时,我们会得到一个 Method 对象报告 A 作为它的声明类,它没有实现任何接口。因此,Method 对象无法为您的预期查找携带所需类型的信息。注释搜索方法需要更多的信息,如果它应该找到所有接口方法候选,例如。您可能需要明确传递实际的Class


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

JRE 没有为您的任务类型提供实现。

【讨论】:

  • 嗨,霍尔格。感谢您的全面回答并指出在 Java 中没有单一“声明”这样的概念。只有几件事: 1. “天真的”代码没有打印你所说的内容。 2.我认为如果递归调用它可能会变得不那么天真。
  • 确实如此。我在复制代码后进行了更改,忘记再次复制它。当然,您可以递归地收集所有方法/接口,但是,正如我试图解释的那样,由于可能继承具有相同超类型的多个接口,您可能会多次遇到相同的类型。 然后如果您更喜欢覆盖方法的注释(如果存在),您必须找出哪个概念上覆盖另一个。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-18
  • 1970-01-01
  • 2014-02-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多