2009-12-07 14 views
23

¿Hay alguna manera de obtener una lista de métodos que serían accesibles (no necesariamente públicos) para una clase determinada? El código en cuestión estará en una clase completamente diferente.Obteniendo una lista de métodos accesibles para una clase dada a través de la reflexión

Ejemplo:

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

Para la clase B me gustaría llegar:

  • todos sus propios métodos
  • methodA1 y methodA2 de clase A
  • methodA3 si y sólo si la clase B está en el mismo paquete que A

methodA4 nunca debe incluirse en los resultados porque es inaccesible para la clase B. Para aclarar una vez más, el código que necesita encontrar y devolver los métodos anteriores estará en una clase/paquete completamente diferente.

Ahora, Class.getMethods() solo devuelve métodos públicos y, por lo tanto, no hará lo que yo quiera; Class.getDeclaredMethods() solo devuelve métodos para la clase actual. Aunque sin duda puedo usar el último y recorrer la jerarquía de clases comprobando manualmente las reglas de visibilidad, prefiero no hacerlo si hay una solución mejor. ¿Me estoy perdiendo algo obvio aquí?

Respuesta

29

Usa Class.getDeclaredMethods() para obtener una lista de todos los métodos (privados o no) de la clase o interfaz.

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

Nota: esto excluye métodos heredados. Use Class.getMethods() para eso. Devolverá todos los métodos públicos (heredados o no).

Para hacer una lista completa de todo lo que una clase puede acceder (incluidos los métodos heredados), tendrá que recorrer el árbol de clases que se extiende. Entonces:

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

Eso todavía no hace lo que quiero porque también devolverá los métodos privado/privado del paquete. Su código es perfectamente razonable para encontrar métodos anotados; lo que necesito es averiguar en tiempo de ejecución todos los métodos que la instancia 'B' puede invocar. Como dije, puedo escribir el código para hacerlo (similar a lo que publicó más algunos ifs arrojados para verificar la accesibilidad) pero me preguntaba si hay una mejor manera. – ChssPly76

+0

Bueno, no hay una función API estándar para hacerlo así que usted mismo lo está escribiendo. Eso es. Estás haciendo lo que de alguna manera es un requisito inusual. Por lo general, con la reflexión solo le preocupa lo que ** se puede acceder públicamente, de ahí getMethods() y es por eso que existe esa función auxiliar. No existe tal ayudante (estándar) para lo que quieres, por lo tanto, lo estás escribiendo tú mismo. – cletus

+0

Sé que no hay una API estándar; Esperaba que alguien tuviese un problema similar y encontrara una solución, pero supongo que no. No vi nada relacionado con esto en lugares obvios como beanutils/javassist/etc ... tampoco. El código terminó siendo algo complicado, en realidad, al tener que lidiar con métodos sintéticos en clases anidadas, etc ... – ChssPly76

2

Bastante seguro que tendrás que subir las superclases para obtener lo que quieres. Después de todo, eso es lo que getMethods() está haciendo con la llamada getDeclaredMethods() internamente (en cierto modo: en realidad llama a una versión privada que filtra los métodos no públicos, pero recorre el árbol de clases para compilar la lista completa).

Curioso por qué se necesita tal cosa.

5

Como cletus y PSpeed ​​ya han respondido, debe atravesar el árbol de herencia de las clases.

Esta es la manera de hacerlo, pero sin gastos de envío de paquetes métodos privados:

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

lo estoy usando en un comprobador de compatibilidad con versiones anteriores, donde sé que las clases que puedan ser afectados no estarán en el mismo paquete de todos modos.

0

Un punto en la respuesta de Cletus (no puedo comentar allí porque no tengo suficiente reputación).De todos modos, el código de Cletus no funcionaba para mí (Eclipse también se quejaba de ello), probablemente debido a los cambios en Java desde 2009.

La línea:

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

tuvo que ser cambiado a:

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

para obtener cualquier salida. Así que el código completo para mí fue (incluyendo tipos de argumentos de entrada, modificadores y tipo de retorno):

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