Как вызвать метод Java по имени, представленному в виде строки?
У меня есть две переменные:
Object obj;
String methodName = "getName";
Как я могу вызвать метод, указанный в переменной methodName
, на объекте obj
, не зная при этом класс obj
?
Метод, который я хочу вызвать, не принимает параметров и возвращает значение типа String
. Это геттер для Java bean.
5 ответ(ов)
Метод можно вызвать следующим образом. Также существует множество других возможностей (посмотрите API рефлексии), но это самый простой способ:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.junit.Assert;
import org.junit.Test;
public class ReflectionTest {
private String methodName = "length";
private String valueObject = "Некоторый объект";
@Test
public void testGetMethod() throws SecurityException, NoSuchMethodException, IllegalArgumentException,
IllegalAccessException, InvocationTargetException {
Method m = valueObject.getClass().getMethod(methodName, new Class[] {});
Object ret = m.invoke(valueObject, new Object[] {});
Assert.assertEquals(11, ret);
}
}
В этом примере мы используем рефлексию для получения метода length
класса String
, а затем вызываем этот метод на объекте valueObject
. Результат выполнения метода должен равняться 11, что соответствует длине строки "Некоторый объект"
.
Сначала, не стоит так делать. Избегайте подобного кода. Он часто оказывается плохим и небезопасным (см. раздел 6 Руководства по безопасному программированию для языка Java, версия 2.0).
Если вам все же необходимо это сделать, предпочтите использование java.beans вместо рефлексии. Beans оборачивает рефлексию, обеспечивая относительно безопасный и стандартный доступ.
Индексация (ускорение)
Вы можете использовать FunctionalInterface
для хранения методов в контейнере и их индексации. Можно использовать массив для вызова методов по индексу или hashmap для вызова по строковым ключам. С помощью этого подхода вы можете индексировать ваши методы, чтобы вызывать их динамически быстрее.
@FunctionalInterface
public interface Method {
double execute(int number);
}
public class ShapeArea {
private final static double PI = 3.14;
private Method[] methods = {
this::square,
this::circle
};
private double square(int number) {
return number * number;
}
private double circle(int number) {
return PI * number * number;
}
public double run(int methodIndex, int number) {
return methods[methodIndex].execute(number);
}
}
Синтаксис лямбд
Вы также можете использовать синтаксис лямбд:
public class ShapeArea {
private final static double PI = 3.14;
private Method[] methods = {
number -> {
return number * number;
},
number -> {
return PI * number * number;
},
};
public double run(int methodIndex, int number) {
return methods[methodIndex].execute(number);
}
}
Этот подход позволяет вам легко добавлять новые методы для вычисления площадей различных фигур и выполнять их по индексу без дополнительных затрат на условия или явные вызовы.
В коде, который вы привели, происходит вызов метода methodName
у объекта obj
с использованием рефлексии в Java. Давайте разберем его по шагам:
Объявление объекта:
Object obj;
Здесь создается переменная
obj
типаObject
. Пожалуйста, убедитесь, что этот объект инициализирован перед его использованием.Получение метода по имени:
Method method = obj.getClass().getMethod("methodName", null);
В этой строке мы получаем объект
Method
, который представляет методmethodName
класса объектаobj
. Обратите внимание, что второй параметр (null
) указывает, что метод не принимает аргументов. Если вашим методом есть параметры, вам нужно будет указать их типы в виде массива классов.Вызов метода:
method.invoke(obj, null);
Здесь мы вызываем метод
methodName
на объектеobj
. Аргументnull
во втором параметреinvoke
означает, что метод не принимает аргументов.
Если у вас возникнут дополнительные вопросы по использованию рефлексии в Java или по какому-либо другому аспекту, не стесняйтесь задавать!
Для выполнения динамического вызова метода класса по его имени в Java, вы можете использовать следующий код:
// Шаг 1 - Используем строку funClass для получения объекта класса
String funClass = "package.myclass";
Class<?> c = Class.forName(funClass);
// Шаг 2 - Создаем экземпляр объекта указанного класса
Object o = c.newInstance();
// Подготавливаем массив аргументов, которые принимает ваша функция, в данном случае только одну строку
Class<?>[] paramTypes = new Class[1];
paramTypes[0] = String.class;
String methodName = "mymethod";
// Получаем объект метода по имени метода и типам параметров
Method m = c.getDeclaredMethod(methodName, paramTypes);
// Вызываем метод с фактическими параметрами
m.invoke(o, "testparam");
Этот код сначала загружает класс по его имени (в строке funClass
), создает его экземпляр, находит метод с заданным именем и типами параметров, а затем вызывает этот метод с указанными аргументами. Убедитесь, что вы обрабатываете возможные исключения, такие как ClassNotFoundException
, InstantiationException
, IllegalAccessException
и InvocationTargetException
для надежного кода.
Что такое рефлексия и зачем она нужна?
Как создать обобщённый массив в Java?
Получить обобщённый тип класса во время выполнения
Eclipse/Java: не работает автозавершение кода
Загрузка JDK Java на Linux через wget приводит к отображению страницы лицензии вместо установки