Si j'ai un cours comme celui-ci:
public class Whatever
{
public void aMethod(int aParam);
}
y a-t-il un moyen de savoir qui aMethod
utilise un paramètre nommé aParam
, qui est de type int
?
Si j'ai un cours comme celui-ci:
public class Whatever
{
public void aMethod(int aParam);
}
y a-t-il un moyen de savoir qui aMethod
utilise un paramètre nommé aParam
, qui est de type int
?
Réponses:
Résumer:
method.getParameterTypes()
Afin d'écrire une fonctionnalité de saisie semi-automatique pour un éditeur (comme vous l'avez indiqué dans l'un des commentaires), il existe quelques options:
arg0
, arg1
, arg2
etc.intParam
, stringParam
, objectTypeParam
, etc.Dans Java 8, vous pouvez effectuer les opérations suivantes:
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(){}
}
Donc, pour votre classe, Whatever
nous pouvons faire un test manuel:
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;
}
}
}
}
qui devrait s'afficher [aParam]
si vous avez passé l' -parameters
argument à votre compilateur Java 8.
Pour les utilisateurs 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>
Pour plus d'informations, consultez les liens suivants:
La bibliothèque Paranamer a été créée pour résoudre ce même problème.
Il essaie de déterminer les noms de méthodes de différentes manières. Si la classe a été compilée avec le débogage, elle peut extraire les informations en lisant le bytecode de la classe.
Une autre façon est d'injecter un membre statique privé dans le bytecode de la classe après sa compilation, mais avant qu'il ne soit placé dans un fichier jar. Il utilise ensuite la réflexion pour extraire ces informations de la classe au moment de l'exécution.
https://github.com/paul-hammant/paranamer
J'ai eu des problèmes avec cette bibliothèque, mais je l'ai finalement fait fonctionner. J'espère signaler les problèmes au responsable.
ParameterNAmesNotFoundException
voir classe org.springframework.core.DefaultParameterNameDiscoverer
DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
String[] params = discoverer.getParameterNames(MathUtils.class.getMethod("isPrime", Integer.class));
Oui.
Le code doit être compilé avec un compilateur compatible Java 8 avec l'option de stockage des noms de paramètres formels activée ( option -parameters ).
Ensuite, cet extrait de code devrait fonctionner:
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());
}
}
Vous pouvez récupérer la méthode avec réflexion et détecter ses types d'arguments. Vérifiez http://java.sun.com/j2se/1.4.2/docs/api/java/lang/reflect/Method.html#getParameterTypes%28%29
Cependant, vous ne pouvez pas dire le nom de l'argument utilisé.
C'est possible et Spring MVC 3 le fait, mais je n'ai pas pris le temps de voir exactement comment.
La mise en correspondance des noms de paramètres de méthode avec les noms de variables de modèle d'URI ne peut être effectuée que si votre code est compilé avec le débogage activé. Si vous n'avez pas activé le débogage, vous devez spécifier le nom du nom de variable du modèle d'URI dans l'annotation @PathVariable afin de lier la valeur résolue du nom de la variable à un paramètre de méthode. Par exemple:
Tiré de la documentation du printemps
Bien que ce ne soit pas possible (comme d'autres l'ont illustré), vous pouvez utiliser une annotation pour reporter le nom du paramètre et l'obtenir par réflexion.
Ce n'est pas la solution la plus propre, mais elle fait le travail. Certains webservices font en fait cela pour conserver les noms de paramètres (par exemple: déployer des WS avec glassfish).
Voir java.beans.ConstructorProperties , c'est une annotation conçue pour faire exactement cela.
Vous devriez donc pouvoir faire:
Whatever.declaredMethods
.find { it.name == 'aMethod' }
.parameters
.collect { "$it.type : $it.name" }
Mais vous obtiendrez probablement une liste comme celle-ci:
["int : arg0"]
Je pense que cela sera corrigé dans Groovy 2.5+
Alors actuellement, la réponse est:
Voir également:
Pour chaque méthode, alors quelque chose comme:
Whatever.declaredMethods
.findAll { !it.synthetic }
.collect { method ->
println method
method.name + " -> " + method.parameters.collect { "[$it.type : $it.name]" }.join(';')
}
.each {
println it
}
aMethod
. Je veux l'obtenir pour toutes les méthodes d'une classe.
antlr
pour obtenir des noms de paramètres pour cela?
Comme @Bozho l'a déclaré, il est possible de le faire si des informations de débogage sont incluses lors de la compilation. Il y a une bonne réponse ici ...
Comment obtenir les noms de paramètres des constructeurs d'un objet (réflexion)?par @AdamPaynter
... en utilisant la bibliothèque ASM. J'ai rassemblé un exemple montrant comment vous pouvez atteindre votre objectif.
Tout d'abord, commencez par un pom.xml avec ces dépendances.
<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>
Ensuite, cette classe devrait faire ce que vous voulez. Appelez simplement la méthode statique 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;
}
}
Voici un exemple avec un test unitaire.
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) {
}
}
}
Vous pouvez trouver l'exemple complet sur GitHub
static
méthodes. C'est parce que dans ce cas, le nombre d'arguments renvoyés par ASM est différent, mais c'est quelque chose qui peut être facilement corrigé.Les noms de paramètres ne sont utiles que pour le compilateur. Lorsque le compilateur génère un fichier de classe, les noms de paramètres ne sont pas inclus - la liste d'arguments d'une méthode se compose uniquement du nombre et des types de ses arguments. Il serait donc impossible de récupérer le nom du paramètre en utilisant la réflexion (comme indiqué dans votre question) - il n'existe nulle part.
Cependant, si l'utilisation de la réflexion n'est pas une exigence absolue, vous pouvez récupérer ces informations directement à partir du code source (en supposant que vous les ayez).
Parameter names are only useful to the compiler.
Faux. Regardez la bibliothèque Retrofit. Il utilise des interfaces dynamiques pour créer des requêtes d'API REST. L'une de ses fonctionnalités est la possibilité de définir des noms d'espace réservé dans les chemins d'URL et de remplacer ces espaces réservés par les noms de paramètres correspondants.
Pour ajouter mes 2 cents; Les informations de paramètre sont disponibles dans un fichier de classe "pour le débogage" lorsque vous utilisez javac -g pour compiler la source. Et il est disponible pour APT mais vous aurez besoin d'une annotation donc inutile. (Quelqu'un a discuté de quelque chose de similaire il y a 4-5 ans ici: http://forums.java.net/jive/thread.jspa?messageID=13467&tstart=0 )
Dans l'ensemble, vous ne pouvez l'obtenir que si vous travaillez directement sur les fichiers source (similaire à ce que fait APT au moment de la compilation).