通过反射获取给定类的可访问方法列表

37
有没有一种方法可以获取一个类可以访问的方法列表(不一定是public)?涉及到的代码会在一个完全不同的类中。
例如:
public class A {
  public void methodA1();
  protected void methodA2();
  void methodA3();
  private void methodA4();
}

public class B extends A {
  public void methodB1();
  protected void methodB2();
  private void methodB3();
}

对于类B,我想获得以下内容:

  • 所有自己的方法
  • A中的methodA1methodA2
  • 仅当类BA在同一个包中时,才获取methodA3

methodA4永远不应包含在结果中,因为它对类B是不可访问的。再次澄清,需要查找并返回上述方法的代码将位于完全不同的类/包中。

现在,Class.getMethods()仅返回公共方法,因此无法满足我的要求;Class.getDeclaredMethods()仅返回当前类的方法。虽然我可以使用后者并向上遍历类层次结构来手动检查可见性规则,但如果有更好的解决方案,我宁愿不这样做。我是否漏掉了一些明显的东西?

4个回答

41

使用Class.getDeclaredMethods()方法可以获取类或接口中的所有方法列表(包括私有方法)。

Class c = ob.getClass();
for (Method method : c.getDeclaredMethods()) {
  if (method.getAnnotation(PostConstruct.class) != null) {
    System.out.println(method.getName());
  }
}

注意: 这将排除继承的方法。要包括继承的方法,请使用Class.getMethods(),它会返回所有公共方法(无论是否继承)。

要列出一个类可以访问的所有内容(包括继承的方法),您需要遍历它所扩展的类树。 因此:

Class c = ob.getClass();
for (Class c = ob.getClass(); c != null; c = c.getSuperclass()) {
  for (Method method : c.getDeclaredMethods()) {
    if (method.getAnnotation(PostConstruct.class) != null) {
      System.out.println(c.getName() + "." + method.getName());
    }
  }
}

1
那仍然不能满足我的需求,因为它也会返回私有/包私有方法。你的代码对于查找已注释的方法是完全合理的;我需要的是在运行时找到B实例可以调用的所有方法。正如我所说,我可以编写代码来实现这一点(类似于您发布的内容加上一些if语句来检查可访问性),但我想知道是否有更好的方法。 - ChssPly76
好的,没有标准的API函数可以做到这一点,所以你需要自己编写。就是这样,你正在处理一个有些不寻常的需求。通常情况下,使用反射时只关心公共可访问的内容,因此使用getMethods(),这也是为什么存在那个帮助函数的原因。对于你想要的内容,没有这样的(标准)帮助函数,因此你需要自己编写。 - cletus
我知道没有标准的API;我希望有人遇到过类似的问题并找到了解决方案 - 但我猜没有。在像beanutils / javassist /等明显的地方,我没有看到任何相关的内容...实际上,代码变得有些复杂 - 必须处理嵌套类中的合成方法等等... - ChssPly76
对于包私有成员,您可以比较给定类和超类的包名称以确定可访问性。但是我不确定静态方法是否适用。 - Sgene9
听起来你实际上通过自己编码来处理了这个问题。你能描述一下你是如何发现从A类中可访问的静态方法的吗? - Sgene9

6
正如cletus和PSpeed已经回答的那样 - 你需要遍历类的继承树。
这是我处理的方式,但是没有处理包私有方法:
public static Method[] getAccessibleMethods(Class clazz) {
   List<Method> result = new ArrayList<Method>();

   while (clazz != null) {
      for (Method method : clazz.getDeclaredMethods()) {
         int modifiers = method.getModifiers();
         if (Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers)) {
            result.add(method);
         }
      }
      clazz = clazz.getSuperclass();
   }

   return result.toArray(new Method[result.size()]);
}

我正在使用它进行向后兼容性检查,我知道可能受影响的类不会在同一个包中。


2
我相信你需要遍历父类来获取你想要的内容。毕竟,这就是`getMethods()`在内部使用`getDeclaredMethods()`调用的方式(有点...它实际上调用了一个过滤非公共方法的私有版本,但它确实遍历了类树以构建完整列表)。
不过,我很好奇为什么需要这样做。

1
Cletus的答案中有一点需要补充(我不能在那里评论,因为我声望不够)。无论如何,Cletus的代码对我没有起作用(Eclipse也抱怨它),可能是由于自2009年以来Java发生了变化。
这一行:
for (Class c = ob.getClass(); c != null; c = c.getSuperclass()) {

必须更改为:

for (Class<?> c = ob.getClass(); c != null; c = c.getSuperclass()) {

要得到任何输出,必须编写完整的代码(包括输入参数类型、修饰符和返回类型):

for (Class<?> c = scanner.getClass(); c != null; c = c.getSuperclass()) {
    System.out.println(c.getName());            
    for (Method method : c.getMethods()) {
        System.out.println("\t" + Modifier.toString(method.getModifiers()) 
            + " " + method.getName());
        for (Class<?> param: method.getParameterTypes()) {
            System.out.println("\t\t" + param.getName());
        }
        System.out.println("\t\t == returns ==> "
            + method.getReturnType().getName());
    }
  }        

有用的答案 - 仅获取类的方法列表并不那么有用,因为没有参数/返回类型来区分重载方法(相同名称,但参数/返回类型不同)。 - Koka

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接