Подтвердить что ты не робот

Получение списка доступных методов для данного класса посредством отражения

Есть ли способ получить список методов, которые будут доступны (не обязательно общедоступны) определенным классом? Этот код будет находиться в совершенно другом классе.

Пример:

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 я хотел бы получить:

  • все свои методы
  • methodA1 и methodA2 из класса A
  • methodA3 тогда и только тогда, когда класс B находится в том же пакете, что и A

methodA4 никогда не должен включаться в результаты, поскольку он недоступен классу B. Чтобы еще раз прояснить, код, который должен найти и вернуть указанные выше методы, будет в совершенно другом классе/пакете.

Теперь Class.getMethods() возвращает только общедоступные методы и, следовательно, не будет делать то, что я хочу; Class.getDeclaredMethods() возвращает методы только для текущего класса. Хотя я могу, конечно, использовать последнее и пройти иерархию классов, проверяя правила видимости вручную, я бы предпочел, чтобы там было лучшее решение. Я пропустил что-то очевидное здесь?

4b9b3361

Ответ 1

Используйте 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());
    }
  }
}

Ответ 2

Поскольку 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()]);
}

Я использую его в контроле обратной совместимости, где я знаю, что классы, которые могут быть затронуты, в любом случае не будут в одном пакете.

Ответ 3

Довольно уверен, что вам придется подойти к суперклассам, чтобы получить то, что вы хотите. В конце концов, то, что getMethods() делает с вызовом getDeclaredMethods() внутренне (вроде: он фактически вызывает приватную версию, которая отфильтровывает непубличные методы, но она переводит дерево классов для построения полного списка).

Любопытно, почему такая вещь нужна.

Ответ 4

Точка ответа Cletus (я не могу комментировать там, потому что у меня недостаточно репутации.). Во всяком случае, код Cletus не работал у меня (Eclipse также жаловался на это), вероятно, из-за изменений в Java с 2009 года.

Строка:

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