ChssPly76
ChssPly76

Reputation: 100686

Getting a list of accessible methods for a given class via reflection

Is there a way to get a list of methods that would be accessible (not necessarily public) by a given class? The code in question will be in a completely different class.

Example:

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

For class B I'd like to get:

methodA4 should never be included in results because it's inaccessible to class B. To clarify once again, code that needs to find and return the above methods will be in a completely different class / package.

Now, Class.getMethods() only returns public methods and thus won't do what I want; Class.getDeclaredMethods() only returns methods for current class. While I can certainly use the latter and walk the class hierarchy up checking the visibility rules manually, I'd rather not if there's a better solution. Am I missing something glaringly obvious here?

Upvotes: 39

Views: 58560

Answers (4)

PSpeed
PSpeed

Reputation: 3364

Pretty sure you will have to walk up the superclasses to get what you want. After all, that's what getMethods() is doing with the getDeclaredMethods() call internally (sort of... it actually calls a private version that filters out non-public methods but it does traverse up the class tree to build the full list).

Curious why such a thing is needed, though.

Upvotes: 2

Alluir
Alluir

Reputation: 91

A point on Cletus's answer (I can't comment there because I don't have enough reputation.). Anyway, Cletus's code did not work for me (Eclipse was also complaining about it), probably due to changes in Java since 2009.

The line:

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

had to be changed to:

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

to get any output at all. So the complete code for me was (including input argument types, modifiers and return type):

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

Upvotes: 2

cletus
cletus

Reputation: 625007

Use Class.getDeclaredMethods() to get a list of all methods (private or otherwise) from the class or interface.

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

Note: this excludes inherited methods. Use Class.getMethods() for that. It will return all public methods (inherited or not).

To do a comprehensive list of everything a class can access (including inherited methods), you will need to traverse the tree of classes it extends. So:

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

Upvotes: 43

Johan Kaving
Johan Kaving

Reputation: 4939

As cletus and PSpeed has already answered - you need to traverse the inheritance tree of classes.

This is the way I do it, but without handling package private methods:

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

I am using it in a backwards-compatibility checker where I know that the classes that might be affected will not be in the same package anyway.

Upvotes: 6

Related Questions