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.
¿Cómo puedo obtener estos nombres de parámetros de métodos?
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 unMethod
llamando agetDeclaredMethod()
ogetDeclaredMethods()
que devuelve referencias a los objetos deMethod
- Desde el objeto
Method
, call (new asgetParameters()
Java 8)getParameters()
que devuelve una matriz de objetosParameter
- En el objeto
Parameter
, llame agetName()
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>
según Store información sobre los parámetros del método (utilizable mediante la reflexión) en intellij 13 , el equivalente de "javac -parameters" dentro de Eclipse IDE es ''Almacenar información sobre los parámetros del método (utilizables mediante reflexión)'' en Ventana -> Preferencias -> Java - > Compilador.