2010-04-28 18 views
16

decir te alguna manera tengo una referencia de objeto a partir de una otra clase:¿Cómo obtener los nombres de los parámetros de los constructores de un objeto (reflejo)?

Object myObj = anObject; 

Ahora puede obtener la clase de este objeto:

Class objClass = myObj.getClass(); 

Ahora, puedo conseguir todos los constructores de esta clase:

Constructor[] constructors = objClass.getConstructors(); 

Ahora, puede recorrer cada constructor:

if (constructors.length > 0) 
{ 
    for (int i = 0; i < constructors.length; i++) 
    { 
     System.out.println(constructors[i]); 
    } 
} 

Esto ya me está dando un buen resumen del constructor, por ejemplo, se muestra una prueba pública constructor (String paramName) como prueba pública (java.lang.String)

En vez de darme el tipo de clase sin embargo, Quiero obtener el nombre del parámetro ... en este caso "paramName". ¿Como podría hacerlo? Intenté lo siguiente sin éxito:

if (constructors.length > 0) 
    { 
     for (int iCon = 0; iCon < constructors.length; iCon++) 
     { 
      Class[] params = constructors[iCon].getParameterTypes(); 
      if (params.length > 0) 
      { 
       for (int iPar = 0; iPar < params.length; iPar++) 
       { 
        Field fields[] = params[iPar].getDeclaredFields(); 
        for (int iFields = 0; iFields < fields.length; iFields++) 
        { 
         String fieldName = fields[i].getName(); 
         System.out.println(fieldName); 
        }          
       } 
      } 
     } 
    } 

Lamentablemente, esto no me está dando el resultado esperado. ¿Alguien podría decirme cómo debo hacer esto o qué estoy haciendo mal? ¡Gracias!

+1

Esto es posible a través de la reflexión en ** Java 8 **, vea [esta respuesta SO] (http://stackoverflow.com/a/21455958/573057) - encontrado leyendo la documentación en [paranamer] (https: //github.com/paul-hammant/paranamer) de la respuesta de Duncan McGregor a continuación. – earcam

Respuesta

11

Esta información se pierde después de la compilación y no se puede recuperar en tiempo de ejecución.

+0

¿Estás seguro? ¿Entonces un constructor es tratado de manera diferente que otros métodos? – Tom

+0

@Tom: Ni los constructores ni los métodos retienen los nombres de los parámetros después de la compilación. –

+0

En http://stackoverflow.com/questions/471693/using-reflection-to-get-method-name-and-parameters Jon Skeet dice en una respuesta "No se pueden obtener los valores de parámetros de método de la reflexión. Se puede obtener los nombres y tipos de parámetros, pero no los parámetros mismos ". - Esto es lo que me hizo creer que era posible, pero creo que estaba equivocado. Gracias sin embargo. – Tom

16

Como se ha mencionado en los comentarios sobre Roman's answer, los nombres de los parámetros pueden ser recuperados si el compilador incluye símbolos de depuración, aunque no a través de la API de reflexión estándar de Java. A continuación se muestra un ejemplo que ilustra cómo se puede obtener los nombres de parámetros a través de los símbolos de depuración mediante el ASM bytecode library:

/** 
* Returns a list containing one parameter name for each argument accepted 
* by the given constructor. If the class was compiled with debugging 
* symbols, the parameter names will match those provided in the Java source 
* code. Otherwise, a generic "arg" parameter name is generated ("arg0" for 
* the first argument, "arg1" for the second...). 
* 
* This method relies on the constructor's class loader to locate the 
* bytecode resource that defined its class. 
* 
* @param constructor 
* @return 
* @throws IOException 
*/ 
public static List<String> getParameterNames(Constructor<?> constructor) throws IOException { 
    Class<?> declaringClass = constructor.getDeclaringClass(); 
    ClassLoader declaringClassLoader = declaringClass.getClassLoader(); 

    Type declaringType = Type.getType(declaringClass); 
    String constructorDescriptor = Type.getConstructorDescriptor(constructor); 
    String url = declaringType.getInternalName() + ".class"; 

    InputStream classFileInputStream = declaringClassLoader.getResourceAsStream(url); 
    if (classFileInputStream == null) { 
     throw new IllegalArgumentException("The constructor's class loader cannot find the bytecode that defined the constructor's class (URL: " + url + ")"); 
    } 

    ClassNode classNode; 
    try { 
     classNode = new ClassNode(); 
     ClassReader classReader = new ClassReader(classFileInputStream); 
     classReader.accept(classNode, 0); 
    } finally { 
     classFileInputStream.close(); 
    } 

    @SuppressWarnings("unchecked") 
    List<MethodNode> methods = classNode.methods; 
    for (MethodNode method : methods) { 
     if (method.name.equals("<init>") && method.desc.equals(constructorDescriptor)) { 
      Type[] argumentTypes = Type.getArgumentTypes(method.desc); 
      List<String> parameterNames = new ArrayList<String>(argumentTypes.length); 

      @SuppressWarnings("unchecked") 
      List<LocalVariableNode> localVariables = method.localVariables; 
      for (int i = 0; i < argumentTypes.length; i++) { 
       // The first local variable actually represents the "this" object 
       parameterNames.add(localVariables.get(i + 1).name); 
      } 

      return parameterNames; 
     } 
    } 

    return null; 
} 

este ejemplo se utiliza la biblioteca de tree API ASM. Si la velocidad y la memoria son valiosas, puede refactorizar el ejemplo para usar su visitor API.

12

Trate https://github.com/paul-hammant/paranamer

Oh por amor de Dios Así que, realmente, vas a hacer que me entro al menos 30 caracteres para editar una respuesta existente para que sea correcta.

+0

¡Gracias por la sugerencia! Funciona genial. – Ciddan

+0

El enlace requiere un inicio de sesión. – javamonkey79

+0

Codehaus se cerró, han señalado a la nueva casa de Github –

Cuestiones relacionadas