W3docs

Java Reflection: Invocação de Métodos

Inspecione e invoque métodos via reflection em Java com a classe Method.

Um objeto Method descreve um método e — crucialmente — permite chamá-lo: method.invoke(target, args...). Este é o coração reflexivo dos executores de testes (encontrar métodos @Test e invocá-los), de frameworks que despacham para handlers por nome e de bridges de scripting. Este capítulo abrange a busca de métodos, a correspondência de tipos de parâmetros que confunde a todos, a invocação de métodos de instância e estáticos, os valores de retorno e como as exceções são encapsuladas.

Se você é novo em reflection, comece com a introdução ao reflection e o capítulo sobre o objeto de classe, pois tudo aqui começa a partir de um Class<?>. A leitura e escrita de membros de dados funciona da mesma forma e é abordada em reflection em campos.

Encontrando métodos

Você pesquisa um método pelo nome mais os tipos dos parâmetros — os tipos de parâmetros são como o Java distingue sobrecargas:

Class<?> c = Calc.class;

Method m1 = c.getMethod("add", int.class, int.class);          // public, incl. inherited
Method m2 = c.getDeclaredMethod("secret", String.class);       // any access, this class only

Method[] pub = c.getMethods();           // all public methods, incl. Object's and inherited
Method[] own = c.getDeclaredMethods();   // all access levels, declared here only

Os objetos Class dos parâmetros devem corresponder aos tipos de parâmetros declarados exatamente — não há resolução de sobrecarga nem alargamento. getMethod("add", Integer.class, Integer.class) não encontrará add(int, int); você deve passar int.class. Uma combinação incorreta lança NoSuchMethodException. Para um método sem argumentos, não passe argumentos de classe: getMethod("toString").

Invocando: instância, estático e argumentos

invoke recebe o objeto alvo primeiro, depois os argumentos como um varargs Object[]:

Calc calc = new Calc();
Method add = Calc.class.getMethod("add", int.class, int.class);
Object result = add.invoke(calc, 2, 3);     // → Integer 5 (autoboxed)
int sum = (int) result;                       // unbox manually

Para um método estático, o alvo é ignorado — passe null:

Method parse = Integer.class.getMethod("parseInt", String.class);
Object n = parse.invoke(null, "42");          // → Integer 42

Os argumentos primitivos são autoboxed no Object[]; o runtime os desencapsula para corresponder aos parâmetros primitivos. O valor de retorno é sempre Object — os primitivos voltam encapsulados, e métodos void retornam null.

Como as exceções surgem: InvocationTargetException

Este é o principal ponto de atenção. Se o método invocado lançar uma exceção, invoke não propaga essa exceção diretamente. Ela é encapsulada em um InvocationTargetException, e você recupera a real com getCause():

try {
  riskyMethod.invoke(target);
} catch (InvocationTargetException e) {
  Throwable real = e.getCause();    // the exception the method actually threw
  // handle 'real', not 'e'
}

As outras exceções verificadas dizem respeito à chamada de reflection em si, não ao corpo do método:

  • IllegalAccessException — o método é inacessível e você não chamou setAccessible(true).
  • IllegalArgumentException — número ou tipos de argumentos errados, ou tipo de alvo incorreto.
  • NoSuchMethodException — lançada no momento da busca, não no momento da invocação.

Portanto: falhas de busca e erros de argumento lançam "diretamente", mas qualquer coisa que o próprio código do método lançar fica encapsulada dentro de InvocationTargetException.

Tipos de retorno, varargs e generics

  • Metadados do tipo de retorno: m.getReturnType() (Class apagado) e m.getGenericReturnType() (Type, mantém generics).
  • Parâmetros: m.getParameterTypes(), m.getParameterCount() e m.getParameters() (nomes disponíveis se compilado com -parameters).
  • Varargs: um parâmetro String... é na verdade String[]. Pesquise-o com getMethod("f", String[].class) e invoque passando um array real, ou confie no fato de que invoke aceitará um array final para o slot varargs.
  • Métodos bridge/sintéticos: classes genéricas geram métodos bridge ocultos; filtre-os com m.isBridge() / m.isSynthetic() ao enumerar.

Um exemplo prático: um mini dispatcher de comandos

O programa constrói um pequeno dispatcher que mapeia comandos em string para métodos em um objeto de serviço, os invoca reflexivamente com argumentos parseados, lida com um método que lança exceção (para mostrar o desencapsulamento do InvocationTargetException) e chama uma factory static com um alvo null.

java— editable, runs on the server

O que extrair da execução:

  • A factory estática foi chamada com factory.invoke(null) — para um método static, o objeto alvo é irrelevante, portanto null é a convenção. O dispatcher então reutilizou o mesmo mecanismo invoke para métodos de instância, passando o calc real como alvo. Uma API, dois tipos de método.
  • divide(1, 0) não lançou ArithmeticException diretamente de invoke. Ele lançou InvocationTargetException, e a ArithmeticException: / by zero genuína foi encontrada via getCause(). Todo framework que chama código do usuário reflexivamente precisa desencapsular isso; esquecer é o motivo pelo qual às vezes você vê um confuso InvocationTargetException em um stack trace em vez do erro real.
  • Pesquisar add com parâmetros Integer.class falhou com NoSuchMethodException mesmo com add(int,int) existindo. A reflection corresponde aos tipos de parâmetros exatamente, sem boxing ou alargamento — int.class e Integer.class são chaves diferentes. Este é o bug de reflection mais comum e o motivo pelo qual os literais .class primitivos importam.
  • O método private secret só pôde ser invocado após getDeclaredMethod + setAccessible(true). Assim como com campos, a variante de busca (getDeclared…) e a porta de acesso (setAccessible) são duas etapas independentes; você precisa de ambas para acessar um membro privado.
  • Os valores de retorno chegaram como Object e foram convertidos no local de chamada ((Calculator) factory.invoke(...)), enquanto o int de add voltou autoboxed como Integer. A reflection não tem conhecimento estático dos tipos de retorno, portanto o chamador é responsável pela conversão — e uma conversão errada se manifesta como ClassCastException em tempo de execução, não em tempo de compilação.

Prática

Prática
Você invoca um método reflexivamente com 'm.invoke(obj)', e o corpo do método lança uma 'IllegalStateException'. No seu código chamador, qual exceção você realmente captura e como chega à 'IllegalStateException'?
Você invoca um método reflexivamente com 'm.invoke(obj)', e o corpo do método lança uma 'IllegalStateException'. No seu código chamador, qual exceção você realmente captura e como chega à 'IllegalStateException'?
Was this page helpful?