Méthodes d’appel au moment de l’exécution utilisant Java Reflection

Méthodes d'appel au moment de l'exécution à l'aide de Java Reflection

1. Vue d'ensemble

Dans ce court article, nous allons jeter un rapide coup d'œil sur la façon de faireinvoke methods at runtime using the Java Reflection API.

2. Se préparer

Créons une classe simple que nous utiliserons pour les exemples qui suivent:

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. Obtention d'un objetMethod

Tout d'abord, nous devons obtenir un objetMethod qui reflète la méthode que nous voulons invoquer. L'objetClass, représentant le type dans lequel la méthode est définie, fournit deux manières de le faire.

3.1. getMethod()

Nous pouvons utilisergetMethod() pour trouver n'importe quelle méthode publique, qu'elle soit statique ou une instance définie dans la classe ou l'une de ses superclasses.

Il reçoit le nom de la méthode comme premier argument, suivi des types des arguments de la méthode:

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

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

3.2. getDeclaredMethod()

Nous pouvons utilisergetDeclaredMethod() pour obtenir n'importe quelle méthode définie dans la classe. Cela inclut les méthodes publiques, protégées, d'accès par défaut et même privées, mais exclut les méthodes héritées.

Il reçoit les mêmes paramètres quegetMethod():

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

4. Invoquer des méthodes

Avec l'instanceMethod en place, nous pouvons maintenant appelerinvoke() pour exécuter la méthode sous-jacente et obtenir l'objet retourné.

4.1. Méthodes d'instance

Pour appeler une méthode d'instance, le premier argument deinvoke() doit être une instance deMethod qui reflète la méthode appelée:

@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éthodes statiques

Étant donné que ces méthodes ne nécessitent pas d'appeler une instance, nous pouvons passernull comme premier argument:

@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. Accessibilité de la méthode

By default, not all reflected methods are accessible. Cela signifie que la machine virtuelle Java applique les vérifications de contrôle d'accès lors de leur appel.

Par exemple, si nous essayons d'appeler une méthode privée en dehors de sa classe de définition ou une méthode protégée depuis l'extérieur d'une sous-classe ou du package de sa classe, nous obtiendrons unIllegalAccessException:

@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 checks et nous permet d'appeler la méthode sans lever d'exception:

@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. Conclusion

Dans cet article rapide, nous avons vu comment appeler des méthodes d'instance et statiques d'une classe au moment de l'exécution via la réflexion. Nous avons également montré comment modifier l'indicateur accessible sur les objets de méthode reflétés pour supprimer les contrôles de contrôle d'accès Java lors de l'appel de méthodes privées et protégées.

Comme toujours, l'exemple de code peut être trouvéover on Github.