views:

104

answers:

3

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:

  • all of its own methods
  • methodA1 and methodA2 from class A
  • methodA3 if and only if class B is in the same package as A

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?

+1  A: 

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());
    }
  }
}
cletus
That still doesn't do what I want because it will return private / package private methods as well. Your code is perfectly reasonable for finding annotated methods; what I need is to find out at run time all the methods that `B` instance can invoke. As I said, I can write the code to do so (similar to what you posted plus a few ifs thrown in to check accessibility) but I was wondering if there's a better way.
ChssPly76
Well theres no standard API function to do it so you're writing it yourself. That's it. You're doing what is somewhat an unusual requirement. Usually with reflection you're only concerned with what is **publicly** accessible, hence getMethods() and that's why that helper function exists. No such (standard) helper exists for what you want hence you're writing it yourself.
cletus
I know there's no standard API; I was hoping someone had a similar problem and found a solution - but I guess not. I didn't see anything related to this in obvious places like beanutils / javassist / etc... either. Code ended up being somewhat involved, actually - having to deal with synthetic methods in nested classes, etc...
ChssPly76
+2  A: 

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.

PSpeed
A: 

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.

Johan Kaving