Se ho una classe come questa:
public class Whatever
{
public void aMethod(int aParam);
}
c'è un modo per sapere che aMethod
utilizza un parametro denominato aParam
, che è di tipo int
?
Se ho una classe come questa:
public class Whatever
{
public void aMethod(int aParam);
}
c'è un modo per sapere che aMethod
utilizza un parametro denominato aParam
, che è di tipo int
?
Risposte:
Riassumere:
method.getParameterTypes()
Per il bene di scrivere funzionalità di completamento automatico per un editor (come hai affermato in uno dei commenti) ci sono alcune opzioni:
arg0
, arg1
, arg2
etc.intParam
, stringParam
, objectTypeParam
, etc.In Java 8 puoi fare quanto segue:
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;
public final class Methods {
public static List<String> getParameterNames(Method method) {
Parameter[] parameters = method.getParameters();
List<String> parameterNames = new ArrayList<>();
for (Parameter parameter : parameters) {
if(!parameter.isNamePresent()) {
throw new IllegalArgumentException("Parameter names are not present!");
}
String parameterName = parameter.getName();
parameterNames.add(parameterName);
}
return parameterNames;
}
private Methods(){}
}
Quindi per la tua classe Whatever
possiamo fare un test manuale:
import java.lang.reflect.Method;
public class ManualTest {
public static void main(String[] args) {
Method[] declaredMethods = Whatever.class.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
if (declaredMethod.getName().equals("aMethod")) {
System.out.println(Methods.getParameterNames(declaredMethod));
break;
}
}
}
}
che dovrebbe essere stampato [aParam]
se hai passato un -parameters
argomento al tuo compilatore Java 8.
Per gli utenti Maven:
<properties>
<!-- PLUGIN VERSIONS -->
<maven-compiler-plugin.version>3.1</maven-compiler-plugin.version>
<!-- OTHER PROPERTIES -->
<java.version>1.8</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<!-- Original answer -->
<compilerArgument>-parameters</compilerArgument>
<!-- Or, if you use the plugin version >= 3.6.2 -->
<parameters>true</parameters>
<testCompilerArgument>-parameters</testCompilerArgument>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
Per ulteriori informazioni vedere i seguenti link:
La libreria Paranamer è stata creata per risolvere questo stesso problema.
Prova a determinare i nomi dei metodi in diversi modi. Se la classe è stata compilata con il debugging può estrarre le informazioni leggendo il bytecode della classe.
Un altro modo è che inserisca un membro statico privato nel bytecode della classe dopo che è stato compilato, ma prima che venga inserito in un jar. Quindi utilizza la riflessione per estrarre queste informazioni dalla classe in fase di esecuzione.
https://github.com/paul-hammant/paranamer
Ho avuto problemi con questa libreria, ma alla fine l'ho fatta funzionare. Spero di segnalare i problemi al manutentore.
ParameterNAmesNotFoundException
vedere la classe org.springframework.core.DefaultParameterNameDiscoverer
DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
String[] params = discoverer.getParameterNames(MathUtils.class.getMethod("isPrime", Integer.class));
Sì.
Il codice deve essere compilato con un compilatore conforme a Java 8 con l'opzione per memorizzare i nomi dei parametri formali attivata ( opzione -parameters ).
Quindi questo snippet di codice dovrebbe funzionare:
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());
}
}
Puoi recuperare il metodo con la reflection e rilevare i suoi tipi di argomento. Controlla http://java.sun.com/j2se/1.4.2/docs/api/java/lang/reflect/Method.html#getParameterTypes%28%29
Tuttavia, non puoi dire il nome dell'argomento utilizzato.
È possibile e Spring MVC 3 lo fa, ma non mi sono preso il tempo per vedere esattamente come.
La corrispondenza dei nomi dei parametri del metodo con i nomi delle variabili del modello URI può essere eseguita solo se il codice è compilato con il debug abilitato. Se il debug non è abilitato, è necessario specificare il nome della variabile del modello URI nell'annotazione @PathVariable per associare il valore risolto del nome della variabile a un parametro del metodo. Per esempio:
Tratto dalla documentazione primaverile
Sebbene non sia possibile (come altri hanno illustrato), è possibile utilizzare un'annotazione per trasferire il nome del parametro e ottenerlo tramite riflessione.
Non è la soluzione più pulita, ma porta a termine il lavoro. Alcuni servizi web in realtà lo fanno per mantenere i nomi dei parametri (es .: distribuire WS con glassfish).
Vedi java.beans.ConstructorProperties , è un'annotazione progettata per fare esattamente questo.
Quindi dovresti essere in grado di fare:
Whatever.declaredMethods
.find { it.name == 'aMethod' }
.parameters
.collect { "$it.type : $it.name" }
Ma probabilmente otterrai una lista in questo modo:
["int : arg0"]
Credo che questo verrà risolto in Groovy 2.5+
Quindi, attualmente, la risposta è:
Guarda anche:
Per ogni metodo, quindi qualcosa come:
Whatever.declaredMethods
.findAll { !it.synthetic }
.collect { method ->
println method
method.name + " -> " + method.parameters.collect { "[$it.type : $it.name]" }.join(';')
}
.each {
println it
}
aMethod
. Voglio ottenerlo per tutti i metodi in una classe.
antlr
per ottenere i nomi dei parametri per questo?
Come affermato da @Bozho, è possibile farlo se le informazioni di debug sono incluse durante la compilazione. C'è una buona risposta qui ...
Come ottenere i nomi dei parametri dei costruttori di un oggetto (riflessione)? di @AdamPaynter
... utilizzando la libreria ASM. Ho messo insieme un esempio che mostra come puoi raggiungere il tuo obiettivo.
Prima di tutto, inizia con un pom.xml con queste dipendenze.
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-all</artifactId>
<version>5.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
Quindi, questa classe dovrebbe fare quello che vuoi. Basta invocare il metodo statico getParameterNames()
.
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.LocalVariableNode;
import org.objectweb.asm.tree.MethodNode;
public class ArgumentReflection {
/**
* 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 theMethod
* @return
* @throws IOException
*/
public static List<String> getParameterNames(Method theMethod) throws IOException {
Class<?> declaringClass = theMethod.getDeclaringClass();
ClassLoader declaringClassLoader = declaringClass.getClassLoader();
Type declaringType = Type.getType(declaringClass);
String constructorDescriptor = Type.getMethodDescriptor(theMethod);
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(theMethod.getName()) && 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 = 1; i <= argumentTypes.length; i++) {
// The first local variable actually represents the "this"
// object if the method is not static!
parameterNames.add(localVariables.get(i).name);
}
return parameterNames;
}
}
return null;
}
}
Ecco un esempio con uno unit test.
public class ArgumentReflectionTest {
@Test
public void shouldExtractTheNamesOfTheParameters3() throws NoSuchMethodException, SecurityException, IOException {
List<String> parameterNames = ArgumentReflection
.getParameterNames(Clazz.class.getMethod("callMe", String.class, String.class));
assertEquals("firstName", parameterNames.get(0));
assertEquals("lastName", parameterNames.get(1));
assertEquals(2, parameterNames.size());
}
public static final class Clazz {
public void callMe(String firstName, String lastName) {
}
}
}
Puoi trovare l'esempio completo su GitHub
static
metodi. Questo perché in questo caso il numero di argomenti restituiti da ASM è diverso, ma è qualcosa che può essere facilmente risolto.I nomi dei parametri sono utili solo per il compilatore. Quando il compilatore genera un file di classe, i nomi dei parametri non vengono inclusi: l'elenco degli argomenti di un metodo consiste solo del numero e dei tipi dei suoi argomenti. Quindi sarebbe impossibile recuperare il nome del parametro usando la riflessione (come etichettato nella tua domanda) - non esiste da nessuna parte.
Tuttavia, se l'uso della reflection non è un requisito fondamentale, puoi recuperare queste informazioni direttamente dal codice sorgente (supponendo che tu lo abbia).
Parameter names are only useful to the compiler.
Sbagliato. Guarda la libreria Retrofit. Utilizza interfacce dinamiche per creare richieste API REST. Una delle sue caratteristiche è la capacità di definire i nomi dei segnaposto nei percorsi dell'URL e sostituire quei segnaposto con i nomi dei parametri corrispondenti.
Aggiungere i miei 2 centesimi; le informazioni sui parametri sono disponibili in un file di classe "per il debug" quando si utilizza javac -g per compilare il sorgente. Ed è disponibile per APT ma avrai bisogno di un'annotazione quindi non ti serve. (Qualcuno ha discusso qualcosa di simile 4-5 anni fa qui: http://forums.java.net/jive/thread.jspa?messageID=13467&tstart=0 )
Nel complesso, in breve, non puoi ottenerlo a meno che tu non lavori direttamente sui file Source (simile a quello che fa APT in fase di compilazione).