Métodos de chamada em tempo de execução usando o Java Reflection

Métodos de chamada em tempo de execução usando o Java Reflection

1. Visão geral

Neste breve artigo, daremos uma olhada rápida em comoinvoke methods at runtime using the Java Reflection API.

2. Preparando-se

Vamos criar uma classe simples que usaremos nos exemplos a seguir:

public class Operations {
    public double publicSum(int a, double b) {
        return a + b;
    }

    public static double publicStaticMultiply(float a, long b) {
        return a * b;
    }

    private boolean privateAnd(boolean a, boolean b) {
        return a && b;
    }

    protected int protectedMax(int a, int b) {
        return a > b ? a : b;
    }
}

3. Obtendo um objetoMethod

Primeiro, precisamos obter um objetoMethod que reflita o método que queremos invocar. O objetoClass, representando o tipo em que o método é definido, fornece duas maneiras de fazer isso.

3.1. getMethod()

Podemos usargetMethod() para encontrar qualquer método público, seja estático ou instância definida na classe ou em qualquer uma de suas superclasses.

Ele recebe o nome do método como o primeiro argumento, seguido pelos tipos de argumentos do método:

Method sumInstanceMethod
  = Operations.class.getMethod("publicSum", int.class, double.class);

Method multiplyStaticMethod
  = Operations.class.getMethod(
    "publicStaticMultiply", float.class, long.class);

3.2. getDeclaredMethod()

Podemos usargetDeclaredMethod() para obter qualquer método definido na classe. Isso inclui métodos públicos, protegidos, de acesso padrão e até privados, mas exclui os herdados.

Ele recebe os mesmos parâmetros degetMethod():

Method andPrivateMethod
  = Operations.class.getDeclaredMethod(
    "privateAnd", boolean.class, boolean.class);
Method maxProtectedMethod
  = Operations.class.getDeclaredMethod("protectedMax", int.class, int.class);

4. Invocando Métodos

Com a instânciaMethod no lugar, podemos agora chamarinvoke() para executar o método subjacente e obter o objeto retornado.

4.1. Métodos de instância

Para invocar um método de instância, o primeiro argumento parainvoke() deve ser uma instância deMethod que reflete o método que está sendo invocado:

@Test
public void givenObject_whenInvokePublicMethod_thenCorrect() {
    Method sumInstanceMethod
      = Operations.class.getMethod("publicSum", int.class, double.class);

    Operations operationsInstance = new Operations();
    Double result
      = (Double) sumInstanceMethod.invoke(operationsInstance, 1, 3);

    assertThat(result, equalTo(4.0));
}

4.2. Métodos estáticos

Como esses métodos não exigem que uma instância seja chamada, podemos passarnull como o primeiro argumento:

@Test
public void givenObject_whenInvokeStaticMethod_thenCorrect() {
    Method multiplyStaticMethod
      = Operations.class.getDeclaredMethod(
        "publicStaticMultiply", float.class, long.class);

    Double result
      = (Double) multiplyStaticMethod.invoke(null, 3.5f, 2);

    assertThat(result, equalTo(7.0));
}

5. Acessibilidade do método

By default, not all reflected methods are accessible. Isso significa que a JVM força as verificações de controle de acesso ao chamá-las.

Por exemplo, se tentarmos chamar um método privado fora de sua classe de definição ou um método protegido de fora de uma subclasse ou pacote de sua classe, obteremos umIllegalAccessException:

@Test(expected = IllegalAccessException.class)
public void givenObject_whenInvokePrivateMethod_thenFail() {
    Method andPrivateMethod
      = Operations.class.getDeclaredMethod(
        "privateAnd", boolean.class, boolean.class);

    Operations operationsInstance = new Operations();
    Boolean result
      = (Boolean) andPrivateMethod.invoke(operationsInstance, true, false);

    assertFalse(result);
}

@Test(expected = IllegalAccessException.class)
public void givenObject_whenInvokeProtectedMethod_thenFail() {
    Method maxProtectedMethod
      = Operations.class.getDeclaredMethod(
        "protectedMax", int.class, int.class);

    Operations operationsInstance = new Operations();
    Integer result
      = (Integer) maxProtectedMethod.invoke(operationsInstance, 2, 4);

    assertThat(result, equalTo(4));
}

By calling setAccesible(true) on a reflected method object, the JVM suppresses the access control checkse permite invocar o método sem lançar uma exceção:

@Test
public void givenObject_whenInvokePrivateMethod_thenCorrect() {
    // ...
    andPrivateMethod.setAccessible(true);
    // ...
    Boolean result
      = (Boolean) andPrivateMethod.invoke(operationsInstance, true, false);

    assertFalse(result);
}

@Test
public void givenObject_whenInvokeProtectedMethod_thenCorrect() {
    // ...
    maxProtectedMethod.setAccessible(true);
    // ...
    Integer result
      = (Integer) maxProtectedMethod.invoke(operationsInstance, 2, 4);

    assertThat(result, equalTo(4));
}

6. Conclusão

Neste artigo rápido, vimos como chamar métodos de instância e estáticos de uma classe em tempo de execução por meio de reflexão. Também mostramos como alterar o sinalizador acessível nos objetos de método refletidos para suprimir as verificações de controle de acesso Java ao chamar métodos privados e protegidos.

Como sempre, o código de exemplo pode ser encontradoover on Github.