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

Вызов частного метода в Java

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

1. Обзор

Хотя методы в Java сделаны закрытыми , чтобы предотвратить их вызов из-за пределов класса-владельца, по какой-то причине нам все равно может понадобиться их вызывать.

Чтобы достичь этого, нам нужно обойти элементы управления доступом Java. Это может помочь нам добраться до угла библиотеки или позволить нам протестировать некоторый код, который обычно должен оставаться закрытым.

В этом кратком руководстве мы рассмотрим, как мы можем проверить функциональность метода независимо от его видимости. Мы рассмотрим два разных подхода: Java Reflection API и Spring ReflectionTestUtils .

2. Видимость вне нашего контроля

Для нашего примера воспользуемся служебным классом LongArrayUtil , который работает с длинными массивами. В нашем классе есть два метода indexOf :

public static int indexOf(long[] array, long target) {
return indexOf(array, target, 0, array.length);
}

private static int indexOf(long[] array, long target, int start, int end) {
for (int i = start; i < end; i++) {
if (array[i] == target) {
return i;
}
}
return -1;
}

Предположим, что видимость этих методов изменить нельзя, и все же мы хотим вызвать приватный метод indexOf .

3. API отражения Java

3.1. Поиск метода с отражением

Хотя компилятор не позволяет нам вызывать функцию, которая не видна нашему классу, мы можем вызывать функции через отражение. Во-первых, нам нужно получить доступ к объекту Method , который описывает функцию, которую мы хотим вызвать:

Method indexOfMethod = LongArrayUtil.class.getDeclaredMethod(
  "indexOf", long[].class, long.class, int.class, int.class);

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

Функция может завершиться ошибкой и вызвать исключение, если метод не существует.

3.2. Разрешить доступ к методу

Теперь нам нужно временно повысить видимость метода:

indexOfMethod.setAccessible(true);

Это изменение будет действовать до тех пор, пока JVM не остановится или свойство access не будет возвращено в значение false .

3.3. Вызов метода с отражением

Наконец, мы вызываем метод invoke для `` объекта Method :

int value = (int) indexOfMethod.invoke(
LongArrayUtil.class, someLongArray, 2L, 0, someLongArray.length);

Теперь мы успешно получили доступ к частному методу.

Первым аргументом для вызова является целевой объект, а остальные аргументы должны соответствовать сигнатуре нашего метода. Так как в данном случае наш метод static , а целевой объект — родительский класс — LongArrayUtil . Для вызова методов экземпляра мы бы передали объект, метод которого мы вызываем.

Мы также должны отметить, что invoke возвращает Object , который имеет значение null для функций void и требует приведения к правильному типу, чтобы использовать его.

4. Spring ReflectionTestUtils

Достижение внутренностей классов — распространенная проблема при тестировании. Библиотека тестов Spring предоставляет несколько ярлыков, помогающих модульным тестам достигать классов. Это часто решает проблемы, характерные для модульных тестов, когда тесту требуется доступ к частному полю, экземпляр которого Spring может создавать во время выполнения.

Во- первых, нам нужно добавить зависимость spring-test в наш pom.xml:

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.4</version>
<scope>test</scope>
</dependency>

Теперь мы можем использовать функцию invokeMethod в ReflectionTestUtils , которая использует тот же алгоритм, что и выше, и избавляет нас от написания кода:

int value = ReflectionTestUtils.invokeMethod(
LongArrayUtil.class, "indexOf", someLongArray, 1L, 1, someLongArray.length);

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

5. Соображения

Использование отражения для обхода видимости функции сопряжено с некоторыми рисками и даже может оказаться невозможным. Мы должны учитывать:

  • Разрешит ли Java Security Manager это в нашей среде выполнения
  • Будет ли функция, которую мы вызываем, без проверки во время компиляции продолжать существовать, чтобы мы могли вызывать ее в будущем.
  • Рефакторинг нашего собственного кода, чтобы сделать вещи более наглядными и доступными

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

В этой статье мы рассмотрели, как получить доступ к закрытым методам с помощью Java Reflection API и Spring ReflectionTestUtils .

Как всегда, пример кода для этой статьи можно найти на GitHub .