Получение списка доступных методов для данного класса посредством отражения
Есть ли способ получить список методов, которые будут доступны (не обязательно общедоступны) определенным классом? Этот код будет находиться в совершенно другом классе.
Пример:
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()
возвращает методы только для текущего класса. Хотя я могу, конечно, использовать последнее и пройти иерархию классов, проверяя правила видимости вручную, я бы предпочел, чтобы там было лучшее решение. Я пропустил что-то очевидное здесь?
Ответы
Ответ 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());
}
}