Перейти к основному содержимому

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

· 4 мин. чтения

1. Обзор

В этой короткой статье мы кратко рассмотрим, как вызывать методы во время выполнения с помощью 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 , отражающий метод, который мы хотим вызвать. Объект класса , представляющий тип, в котором определен метод, предоставляет два способа сделать это.

3.1. получитьметод()

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

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

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

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

3.2. получитьобъявленный метод()

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

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

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

4. Вызов методов

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

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

Чтобы вызвать метод экземпляра, первым аргументом для вызова метода должен быть экземпляр метода , отражающий вызываемый метод:

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

По умолчанию доступны не все отраженные методы . Это означает, что 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));
}

5.1. ДоступныйОбъект # setAccessible

Вызывая setAccesible(true) для отраженного объекта метода, JVM подавляет проверки управления доступом и позволяет нам вызывать метод без создания исключения:

@Test
public void givenObject_whenInvokePrivateMethod_thenCorrect() throws Exception {
Method andPrivatedMethod = Operations.class.getDeclaredMethod("privateAnd", boolean.class, boolean.class);
andPrivatedMethod.setAccessible(true);

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

assertFalse(result);
}

5.2. Доступный объект#canAccess

Java 9 поставляется с совершенно новым способом проверки того, может ли вызывающая сторона получить доступ к объекту отраженного метода .

Для этой цели он предоставляет canAccess в качестве замены устаревшего метода isAccessible .

Давайте посмотрим на это в действии:

@Test
public void givenObject_whenInvokePrivateMethod_thenCheckAccess() throws Exception {
Operations operationsInstance = new Operations();
Method andPrivatedMethod = Operations.class.getDeclaredMethod("privateAnd", boolean.class, boolean.class);
boolean isAccessEnabled = andPrivatedMethod.canAccess(operationsInstance);

assertFalse(isAccessEnabled);
}

Мы можем использовать canAccess , чтобы проверить, есть ли у вызывающей стороны доступ к отраженному методу, прежде чем установить флаг ccessible в значение true с помощью setAccessible(true) .

5.3. ДоступныйОбъект#trySetAccessible

trySetAccessible — еще один удобный метод, который мы можем использовать, чтобы сделать отраженный объект доступным.

Преимущество этого нового метода в том, что он возвращает false , если доступ не может быть разрешен . Однако старый метод setAccessible(true) в случае сбоя создает исключение InaccessibleObjectException .

Давайте проиллюстрируем использование метода trySetAccessible :

@Test
public void givenObject_whenInvokePublicMethod_thenEnableAccess() throws Exception {
Operations operationsInstance = new Operations();
Method andPrivatedMethod = Operations.class.getDeclaredMethod("privateAnd", boolean.class, boolean.class);
andPrivatedMethod.trySetAccessible();
boolean isAccessEnabled = andPrivatedMethod.canAccess(operationsInstance);

assertTrue(isAccessEnabled);
}

6. Заключение

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

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