Posso obter o nome do parâmetro do método usando a reflection Java?

Se eu tiver uma aula assim:

public class Whatever { public void aMethod(int aParam); } 

Existe alguma maneira de saber que aMethod usa um parâmetro chamado aParam , que é do tipo int ?

Para resumir:

  • obter nomes de parâmetros é possível se informações de debugging forem incluídas durante a compilation. Veja esta resposta para mais detalhes
  • caso contrário, obter nomes de parâmetros não é possível
  • obter o tipo de parâmetro é possível, usando method.getParameterTypes()

Para escrever a funcionalidade de preenchimento automático de um editor (como você declarou em um dos comentários), existem algumas opções:

  • use arg0 , arg1 , arg2 etc.
  • use intParam , stringParam , objectTypeParam , etc.
  • use uma combinação das opções acima – a primeira para tipos não-primitivos e a segunda para tipos primitivos.
  • não mostre nenhum nome de argumento – apenas os tipos.

No Java 8, você pode fazer o seguinte:

 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 getParameterNames(Method method) { Parameter[] parameters = method.getParameters(); List 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(){} } 

Então, para a sua turma Whatever que podemos fazer um teste manual:

 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; } } } } 

que deve imprimir [aParam] se você tiver passado o parâmetro -parameters para o seu compilador Java 8.

Para usuários do Maven:

   3.1  1.8     org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version}   -parameters  true -parameters ${java.version} ${java.version}     

Para mais informações, consulte os seguintes links:

  1. Tutorial Oficial de Java: Obtendo Nomes dos parameters do Método
  2. JEP 118: Acesso aos nomes de parâmetros no tempo de execução
  3. Javadoc para class de parâmetro

A biblioteca da Paranamer foi criada para resolver este mesmo problema.

Ele tenta determinar nomes de methods de algumas maneiras diferentes. Se a class foi compilada com debugging, pode extrair as informações lendo o bytecode da class.

Outra maneira é injetar um membro estático privado no bytecode da class depois de compilado, mas antes de ser colocado em um jar. Em seguida, ele usa a reflection para extrair essas informações da class no tempo de execução.

https://github.com/paul-hammant/paranamer

Eu tive problemas ao usar essa biblioteca, mas consegui trabalhar no final. Eu estou esperando relatar os problemas para o mantenedor.

Sim.
O código deve ser compilado com o compilador compatível com Java 8 com a opção de armazenar nomes de parâmetros formais ativados (opção -parameters ).
Então este snippet de código deve funcionar:

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

Você pode recuperar o método com reflection e detectar seus tipos de argumentos. Verifique http://java.sun.com/j2se/1.4.2/docs/api/java/lang/reflect/Method.html#getParameterTypes%28%29

No entanto, você não pode dizer o nome do argumento usado.

consulte a class org.springframework.core.DefaultParameterNameDiscoverer

 DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer(); String[] params = discoverer.getParameterNames(MathUtils.class.getMethod("isPrime", Integer.class)); 

É possível e o Spring MVC 3 faz isso, mas não aproveitei para ver exatamente como.

A correspondência de nomes de parâmetros de methods para nomes de variables ​​de modelos URI só pode ser feita se seu código for compilado com a debugging ativada. Se você não tiver a debugging ativada, deverá especificar o nome do nome da variável do modelo URI na anotação @PathVariable para vincular o valor resolvido do nome da variável a um parâmetro do método. Por exemplo:

Extraído da documentação da primavera

Embora não seja possível (como os outros ilustraram), você pode usar uma anotação para transmitir o nome do parâmetro e obter essa reflection.

Não é a solução mais limpa, mas faz o trabalho. Alguns webservices realmente fazem isso para manter nomes de parâmetros (isto é: implementando WSs com glassfish).

Veja java.beans.ConstructorProperties , é uma anotação projetada para fazer exatamente isso.

Então você deveria ser capaz de fazer:

 Whatever.declaredMethods .find { it.name == 'aMethod' } .parameters .collect { "$it.type : $it.name" } 

Mas você provavelmente obterá uma lista assim:

 ["int : arg0"] 

Eu acredito que isso será corrigido no Groovy 2.5+

Então, atualmente, a resposta é:

  • Se é uma class Groovy, então não, você não pode obter o nome, mas você deve poder no futuro.
  • Se é uma class Java compilada sob Java 8, você deve conseguir.

Veja também:


Para cada método, então algo como:

 Whatever.declaredMethods .findAll { !it.synthetic } .collect { method -> println method method.name + " -> " + method.parameters.collect { "[$it.type : $it.name]" }.join(';') } .each { println it } 

se você usar o eclipse, veja a imagem abaixo para permitir que o compilador armazene as informações sobre os parâmetros do método

insira a descrição da imagem aqui

Os nomes dos parâmetros são úteis apenas para o compilador. Quando o compilador gera um arquivo de class, os nomes dos parâmetros não são incluídos – a lista de argumentos de um método consiste apenas no número e nos tipos de seus argumentos. Portanto, seria impossível recuperar o nome do parâmetro usando reflection (como marcado na sua pergunta) – ele não existe em lugar algum.

No entanto, se o uso da reflection não for um requisito difícil, você poderá recuperar essas informações diretamente do código-fonte (supondo que você as tenha).

Para adicionar meus 2 centavos; informações de parâmetro estão disponíveis em um arquivo de class “para debugging” quando você usa o javac -g para compilar a origem. E está disponível para o APT, mas você precisará de uma anotação para não ser útil para você. (Alguém discutiu algo semelhante há 4-5 anos atrás aqui: http://forums.java.net/jive/thread.jspa?messageID=13467&tstart=0 )

No geral, você não pode obtê-lo a menos que trabalhe diretamente nos arquivos de código-fonte (semelhante ao que o APT faz em tempo de compilation).

Como o @Bozho afirmou, é possível fazê-lo se as informações de debugging forem incluídas durante a compilation. Há uma boa resposta aqui …

Como obter os nomes dos parâmetros dos construtores de um object (reflection)? por @AdamPaynter

… usando a biblioteca ASM. Eu coloquei um exemplo mostrando como você pode alcançar seu objective.

Primeiro de tudo, comece com um pom.xml com essas dependencies.

  org.ow2.asm asm-all 5.2   junit junit 4.12 test  

Então, essa class deve fazer o que você quiser. Basta invocar o método estático 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 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 methods = classNode.methods; for (MethodNode method : methods) { if (method.name.equals(theMethod.getName()) && method.desc.equals(constructorDescriptor)) { Type[] argumentTypes = Type.getArgumentTypes(method.desc); List parameterNames = new ArrayList(argumentTypes.length); @SuppressWarnings("unchecked") List 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; } } 

Aqui está um exemplo com um teste de unidade.

 public class ArgumentReflectionTest { @Test public void shouldExtractTheNamesOfTheParameters3() throws NoSuchMethodException, SecurityException, IOException { List 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) { } } } 

Você pode encontrar o exemplo completo no GitHub

Ressalvas

  • Eu mudei levemente a solução original de @AdamPaynter para fazer com que funcionasse para Métodos. Se entendi corretamente, sua solução funciona apenas com construtores.
  • Esta solução não funciona com methods static . Isso ocorre porque, nesse caso, o número de argumentos retornados pelo ASM é diferente, mas é algo que pode ser facilmente corrigido.