Вызовите Методы во время выполнения, используя Отражение Java

Вызовите методы во время выполнения, используя отражение Java

1. обзор

В этой короткой статье мы кратко рассмотрим, как использоватьinvoke methods at runtime using the Java Reflection API.

2. Готовиться

Давайте создадим простой класс, который мы будем использовать для следующих примеров:

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. Получение объектаMethod

Во-первых, нам нужно получить объектMethod, который отражает метод, который мы хотим вызвать. ОбъектClass, представляющий тип, в котором определен метод, предоставляет два способа сделать это.

3.1. getMethod()с

Мы можем использоватьgetMethod() для поиска любого общедоступного метода, будь то статический или экземплярный, который определен в классе или любом из его суперклассов.

Он получает имя метода в качестве первого аргумента, за которым следуют типы аргументов метода:

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

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

3.2. getDeclaredMethod()с

Мы можем использоватьgetDeclaredMethod() для получения любого метода, определенного в классе. Это включает в себя открытый, защищенный, доступ по умолчанию и даже частные методы, но исключает унаследованные.

Он получает те же параметры, что иgetMethod():

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

4. Методы вызова

Имея экземплярMethod на месте, мы теперь можем вызватьinvoke() для выполнения базового метода и получения возвращенного объекта.

4.1. Методы экземпляра

Чтобы вызвать метод экземпляра, первый аргументinvoke() должен быть экземпляромMethod, который отражает вызываемый метод:

@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. Статические Методы

Поскольку эти методы не требуют вызова экземпляра, мы можем передатьnull в качестве первого аргумента:

@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. Доступность метода

By default, not all reflected methods are accessible. Это означает, что JVM применяет проверки контроля доступа при их вызове.

Например, если мы попытаемся вызвать частный метод вне определяющего его класса или защищенный метод извне подкласса или пакета его класса, мы получимIllegalAccessException:

@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 и позволяет нам вызывать метод, не вызывая исключения:

@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. Заключение

В этой быстрой статье мы увидели, как вызывать экземпляры и статические методы класса во время выполнения посредством отражения. Мы также показали, как изменить флаг доступности в отраженных объектах методов, чтобы подавить проверки контроля доступа Java при вызове закрытых и защищенных методов.

Как всегда, пример кода можно найтиover on Github.