valores tipos sentencia retornar retornan referencia que metodos java reflection java-8 method-parameters

tipos - ¿Cómo obtener los nombres de los parámetros de los métodos en Java 8 utilizando la reflexión?



sentencia return java (4)

Java 8 tiene la capacidad de adquirir nombres de parámetros de métodos mediante Reflection API.

  1. ¿Cómo puedo obtener estos nombres de parámetros de métodos?

  2. Según mi conocimiento, los archivos de clase no almacenan nombres de parámetros formales. ¿Cómo puedo obtener estos usando la reflexión?


¿Cómo puedo obtener estos nombres de parámetros de métodos (algunos ejemplos de código)?

Básicamente, necesitas:

  • obtener una referencia a una Class
  • De la Class , obtenga una referencia a un Method llamando a getDeclaredMethod() o getDeclaredMethods() que devuelve referencias a los objetos de Method
  • Desde el objeto Method , call (new as getParameters() Java 8) getParameters() que devuelve una matriz de objetos Parameter
  • En el objeto Parameter , llame a getName()

Class<String> clz = String.class; for (Method m : clz.getDeclaredMethods()) { System.err.println(m.getName()); for (Parameter p : m.getParameters()) { System.err.println(" " + p.getName()); } }

Salida:

... indexOf arg0 indexOf arg0 arg1 ...

También, según mi conocimiento, los archivos .class no almacenan parámetros formales. Entonces, ¿cómo puedo obtenerlos usando la reflexión?

Ver el javadoc para Parameter.getName() :

... Si el nombre del parámetro está presente , este método devuelve el nombre proporcionado por el archivo de clase . De lo contrario , este método sintetiza un nombre de la forma argN , donde N es el índice del parámetro en el descriptor del método que declara el parámetro.

Si un JDK es compatible con esto, es específico de la implementación (como se puede ver en el resultado anterior, la compilación 125 del JDK 8 no lo admite). El formato de archivo de clase admite atributos opcionales que pueden ser utilizados por una implementación JVM / javac específica y que son ignorados por otras implementaciones que no lo admiten.

Tenga en cuenta que incluso podría generar el resultado anterior con arg0 , arg1 , ... con JVM pre Java 8; todo lo que necesita saber es el recuento de parámetros al que se puede acceder a través de Method.getParameterTypes() :

Class<String> clz = String.class; for (Method m : clz.getDeclaredMethods()) { System.err.println(m.getName()); int paramCount = m.getParameterTypes().length; for (int i = 0; i < paramCount; i++) { System.err.println(" arg" + i); } }

Lo nuevo con JDK 8 es que hay una API extendida y la posibilidad de que las JVM proporcionen los nombres de los parámetros reales en lugar de arg0 , arg1 , ...

El soporte de tales características opcionales es posible a través de atributos opcionales que se pueden adjuntar a las diversas estructuras de archivos de clase. Ver 4.6. Métodos para la estructura method_info dentro de un archivo de clase. Ver también 4.7.1. Definición y nombramiento de nuevos atributos en la especificación de JVM.

Como con JDK 8, la versión del archivo de clase se incrementará a 52, también sería posible cambiar el formato del archivo para admitir esta característica.

Consulte también JEP 118: Acceso a nombres de parámetros en tiempo de ejecución para obtener más información y alternativas de implementación. El modelo de implementación propuesto es agregar un atributo opcional que almacena los nombres de los parámetros. Como el formato de archivo de clase ya admite estos atributos opcionales, esto incluso sería posible de modo que los archivos de clase aún puedan ser utilizados por JVM anteriores, donde simplemente se ignorarán según lo exija la especificación:

Las implementaciones de Java Virtual Machine son necesarias para ignorar silenciosamente los atributos que no reconocen.

Actualizar

Según lo sugerido por @assylias, la fuente debe compilarse con la opción de línea de comando javac -parameters para agregar los metadatos para la reflexión del nombre del parámetro al archivo de la clase. Sin embargo, esto solo afectará al código compilado con esta opción; el código anterior aún imprimirá arg0 , arg1 , etc. ya que las bibliotecas de tiempo de ejecución no se compilan con este indicador y, por lo tanto, no contienen las entradas necesarias en los archivos de clase.


Gracias Andreas, pero finalmente obtuve la solución completa de Oracle Tutorials on Method Parameters

Dice,

Puede obtener los nombres de los parámetros formales de cualquier método o constructor con el método java.lang.reflect.Executable.getParameters. (Las clases Método y Constructor amplían la clase Ejecutable y, por lo tanto, heredan el método Executable.getParameters). Sin embargo, los archivos .class no almacenan nombres de parámetros formales de forma predeterminada. Esto se debe a que muchas herramientas que producen y consumen archivos de clase pueden no esperar una mayor huella estática y dinámica de los archivos .class que contienen nombres de parámetros. En particular, estas herramientas tendrían que manejar archivos .class más grandes, y la Máquina Virtual Java (JVM) usaría más memoria. Además, algunos nombres de parámetros, como el secreto o la contraseña, pueden exponer información sobre métodos sensibles a la seguridad.

Para almacenar nombres de parámetros formales en un archivo .class particular y, por lo tanto, habilitar la API de Reflection para recuperar nombres de parámetros formales, compile el archivo fuente con la opción -parameters en el compilador javac .

Cómo compilar

Remember to compile the with the -parameters compiler option

Salida esperada (para un ejemplo completo, visite el enlace mencionado anteriormente)

java MethodParameterSpy ExampleMethods

Este comando imprime lo siguiente:

Number of constructors: 1 Constructor #1 public ExampleMethods() Number of declared constructors: 1 Declared constructor #1 public ExampleMethods() Number of methods: 4 Method #1 public boolean ExampleMethods.simpleMethod(java.lang.String,int) Return type: boolean Generic return type: boolean Parameter class: class java.lang.String Parameter name: stringParam Modifiers: 0 Is implicit?: false Is name present?: true Is synthetic?: false Parameter class: int Parameter name: intParam Modifiers: 0 Is implicit?: false Is name present?: true Is synthetic?: false Method #2 public int ExampleMethods.varArgsMethod(java.lang.String...) Return type: int Generic return type: int Parameter class: class [Ljava.lang.String; Parameter name: manyStrings Modifiers: 0 Is implicit?: false Is name present?: true Is synthetic?: false Method #3 public boolean ExampleMethods.methodWithList(java.util.List<java.lang.String>) Return type: boolean Generic return type: boolean Parameter class: interface java.util.List Parameter name: listParam Modifiers: 0 Is implicit?: false Is name present?: true Is synthetic?: false Method #4 public <T> void ExampleMethods.genericMethod(T[],java.util.Collection<T>) Return type: void Generic return type: void Parameter class: class [Ljava.lang.Object; Parameter name: a Modifiers: 0 Is implicit?: false Is name present?: true Is synthetic?: false Parameter class: interface java.util.Collection Parameter name: c Modifiers: 0 Is implicit?: false Is name present?: true Is synthetic?: false


Puede usar Paranamer lib ( https://github.com/paul-hammant/paranamer )

Código de muestra que funciona para mí:

import com.thoughtworks.paranamer.AnnotationParanamer; import com.thoughtworks.paranamer.BytecodeReadingParanamer; import com.thoughtworks.paranamer.CachingParanamer; import com.thoughtworks.paranamer.Paranamer; Paranamer info = new CachingParanamer(new AnnotationParanamer(new BytecodeReadingParanamer())); Method method = Foo.class.getMethod(...); String[] parameterNames = info.lookupParameterNames(method);

Si usa Maven, ponga esta dependencia en su pom.xml:

<dependency> <groupId>com.thoughtworks.paranamer</groupId> <artifactId>paranamer</artifactId> <version>2.8</version> </dependency>