Aufrufmethoden zur Laufzeit mit Java Reflection

Aufrufen von Methoden zur Laufzeit mithilfe von Java Reflection

1. Überblick

In diesem kurzen Artikel werfen wir einen kurzen Blick aufinvoke methods at runtime using the Java Reflection API.

2. Fertig werden

Erstellen wir eine einfache Klasse, die wir für die folgenden Beispiele verwenden:

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. Erhalten einesMethod-Objekts

Zuerst müssen wir einMethod-Objekt erhalten, das die Methode widerspiegelt, die wir aufrufen möchten. DasClass-Objekt, das den Typ darstellt, in dem die Methode definiert ist, bietet zwei Möglichkeiten, dies zu tun.

3.1. getMethod()

Wir könnengetMethod() verwenden, um eine öffentliche Methode zu finden, sei es statisch oder eine Instanz, die in der Klasse oder einer ihrer Oberklassen definiert ist.

Es erhält den Methodennamen als erstes Argument, gefolgt von den Argumenttypen der Methode:

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

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

3.2. getDeclaredMethod()

Wir könnengetDeclaredMethod() verwenden, um jede in der Klasse definierte Methode abzurufen. Dies umfasst öffentliche, geschützte, Standardzugriffs- und sogar private Methoden, schließt jedoch geerbte Methoden aus.

Es erhält die gleichen Parameter wiegetMethod():

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

4. Methoden aufrufen

Mit der Instanz vonMethodkönnen wir jetztinvoke() aufrufen, um die zugrunde liegende Methode auszuführen und das zurückgegebene Objekt abzurufen.

4.1. Instanzmethoden

Um eine Instanzmethode aufzurufen, muss das erste Argument fürinvoke() eine Instanz vonMethod sein, die die aufgerufene Methode widerspiegelt:

@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. Statische Methoden

Da für diese Methoden keine Instanz aufgerufen werden muss, können wirnull als erstes Argument übergeben:

@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. Zugänglichkeit der Methode

By default, not all reflected methods are accessible. Dies bedeutet, dass die JVM beim Aufrufen Zugriffskontrollprüfungen erzwingt.

Wenn wir beispielsweise versuchen, eine private Methode außerhalb ihrer definierenden Klasse oder eine geschützte Methode außerhalb einer Unterklasse oder ihres Klassenpakets aufzurufen, erhalten wirIllegalAccessException:

@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 und ermöglicht es uns, die Methode aufzurufen, ohne eine Ausnahme auszulösen:

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

In diesem kurzen Artikel haben wir gesehen, wie Instanz- und statische Methoden einer Klasse zur Laufzeit durch Reflektion aufgerufen werden. Wir haben auch gezeigt, wie das Zugriffsflag für die reflektierten Methodenobjekte geändert werden kann, um Java-Zugriffssteuerungsprüfungen beim Aufrufen von privaten und geschützten Methoden zu unterdrücken.

Der Beispielcode befindet sich wie immer inover on Github.