Si j'ai un cours comme celui-ci:
public class Whatever
{
public void aMethod(int aParam);
}
est-il possible de savoir que aMethod
utilise un paramètre nommé aParam
, qui est de type int
?
Résumer:
method.getParameterTypes()
Pour pouvoir écrire des fonctionnalités 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.En 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 imprimer [aParam]
si vous avez passé l'argument -parameters
à votre compilateur Java 8.
Pour les utilisateurs de 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, voir 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 méthode consiste à injecter un membre statique privé dans le bytecode de la classe après sa compilation, mais avant son placement dans un fichier jar. Il utilise ensuite la réflexion pour extraire ces informations de la classe lors de l'exécution.
https://github.com/paul-hammant/paranamer
J'avais des problèmes pour utiliser cette bibliothèque, mais je l'ai finalement fait fonctionner. J'espère signaler les problèmes au responsable.
Oui.
Code doit être compilé avec un compilateur conforme à Java 8 avec option permettant de stocker les noms de paramètres formels activés ( -parameters option).
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());
}
}
voir la classe org.springframework.core.DefaultParameterNameDiscoverer
DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
String[] params = discoverer.getParameterNames(MathUtils.class.getMethod("isPrime", Integer.class));
Vous pouvez récupérer la méthode avec réflexion et détecter ses types d'argument. 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 correspondance des noms de paramètre de méthode to URI Les noms de variable de modèle peuvent. être fait que si votre code est compilé avec le débogage activé. Si vous avez pas de débogage activé, vous devez spécifiez le nom du modèle d'URI nom de variable dans la variable @PathVariable annotation afin de lier le valeur résolue du nom de la variable à un paramètre de méthode. Par exemple:
Tiré de la documentation printanière
Bien que cela ne soit pas possible (comme d’autres l’ont illustré), vous pourriez utiliser une annotation pour reporter le nom du paramètre et obtenir ce résultat par réflexion.
Ce n’est pas la solution la plus propre, mais le travail est fait. Certains services Web le font en fait pour conserver les noms de paramètres (par exemple, le déploiement de WS avec Glassfish).
Voir Java.beans.ConstructorProperties , c'est une annotation conçue pour faire exactement cela.
Donc, vous devriez 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+
Donc, actuellement, la réponse est:
Voir également:
Pour chaque méthode, 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
}
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 aucune utilisation pour vous. (Quelqu'un a discuté d'un sujet similaire il y a 4-5 ans ici: http://forums.Java.net/jive/thread.jspa?messageID=13467&tstart=0 )
Globalement, vous ne pouvez l'obtenir que si vous travaillez directement sur les fichiers source (comme ce que APT fait au moment de la compilation).
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 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. Invoquez 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
. 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 qu'au compilateur. Lorsque le compilateur génère un fichier de classe, les noms de paramètre ne sont pas inclus - la liste d'arguments d'une méthode ne comprend que le nombre et les types de ses arguments. Donc, il serait 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 cette information directement à partir du code source (en supposant que vous l'ayez).