Method.invoke: примеры (JAVA)
Method.invoke(Object obj, Object... args): ObjectОписание Method.invoke() в Java
Метод java.lang.reflect.Method.invoke(Object obj, Object... args) выполняет вызов метода, представленного объектом Method. Используется для динамического вызова методов в рантайме, когда имена или сигнатуры неизвестны на этапе компиляции. Подходит для фреймворков, сериализации, тестирования и прокси.
Сигнатура: public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException.
Параметры и поведение:
obj- экземпляр целевого объекта для вызова нестатического метода. Для статических методов допускаетсяnull.args- аргументы метода какObject[]. Для примитивных параметров аргументы автоматически автозапаковываются (autoboxing). Для varargs-методов можно передать массив соответствующего типа или отдельные элементы (поведение зависит от полученияMethodи JVM).
Возвращаемое значение:
- Возвращается значение метода, автозапакованное для примитивов. Если метод возвращает
void, возвращаетсяnull.
Исключения и особенности:
IllegalAccessException- доступ к методу запрещен (например, приватный метод безsetAccessible(true)или модульные ограничения в Java 9+).IllegalArgumentException- аргументы не соответствуют ожидаемым типам или неверное количество.InvocationTargetException- обертка для исключения, брошенного вызываемым методом; реальная причина доступна черезgetCause().NullPointerException- при попытке вызвать нестатический метод сobj == null.- Безопасность: при активной SecurityManager или в модульной среде (Java 9+) могут применяться дополнительные ограничения. Метод не самый быстрый; при необходимости высокой производительности предпочтительны
MethodHandleили заранее сгенерированные делегаты.
Примечания:
- Для приватных методов обычно вызывается
method.setAccessible(true), но в Java 9+ понадобится открытие модулей (--add-opens) или использованиеMethodHandles.privateLookupIn. - Возвращаемое значение нужно приводить к ожидаемому типу вручную. Неправильный каст приведет к
ClassCastException.
Примеры простого использования Method.invoke()
Ниже приведены короткие примеры с кодом и выводом.
Вызов публичного метода экземпляра
import java.lang.reflect.Method;
public class Example1 {
public String greet(String name) {
return "Hello, " + name;
}
public static void main(String[] args) throws Exception {
Example1 e = new Example1();
Method m = Example1.class.getMethod("greet", String.class);
Object result = m.invoke(e, "Alice");
System.out.println(result);
}
}
Hello, Alice
Вызов статического метода (obj = null)
import java.lang.reflect.Method;
class Util {
public static int sum(int a, int b) { return a + b; }
}
public class Example2 {
public static void main(String[] args) throws Exception {
Method m = Util.class.getMethod("sum", int.class, int.class);
Object r = m.invoke(null, 2, 3);
System.out.println(r); // boxed Integer
}
}
5
Вызов приватного метода с setAccessible
import java.lang.reflect.Method;
class Secret {
private String hidden() { return "secret"; }
}
public class Example3 {
public static void main(String[] args) throws Exception {
Method m = Secret.class.getDeclaredMethod("hidden");
m.setAccessible(true);
Object r = m.invoke(new Secret());
System.out.println(r);
}
}
secret
Когда метод выбрасывает исключение
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
class Fail {
public void boom() { throw new IllegalStateException("boom"); }
}
public class Example4 {
public static void main(String[] args) throws Exception {
Method m = Fail.class.getMethod("boom");
try {
m.invoke(new Fail());
} catch (InvocationTargetException e) {
System.out.println("wrapped: " + e.getCause());
}
}
}
wrapped: java.lang.IllegalStateException: boom
Неправильные аргументы приводят к IllegalArgumentException
import java.lang.reflect.Method;
class A { public void f(int x) {} }
public class Example5 {
public static void main(String[] args) throws Exception {
Method m = A.class.getMethod("f", int.class);
try {
m.invoke(new A(), "str");
} catch (IllegalArgumentException e) {
System.out.println("bad args: " + e.getMessage());
}
}
}
bad args: argument type mismatch
Альтернативные инструменты в Java
Короткое перечисление похожих возможностей и их особенности.
- MethodHandle (java.lang.invoke)
- Constructor.newInstance()
- Proxy + InvocationHandler
- LambdaMetafactory / динамическая генерация байткода
Более производительный способ вызова методов, особенно при многократных вызовах. Поддерживает строгие сигнатуры через invokeExact и более гибкие invoke. Предпочтительнее для производительных реализаций.
Используется для динамического создания экземпляров; похож по семантике, но для конструкторов.
Позволяет перехватывать вызовы интерфейсных методов и реализовать динамическое поведение без прямого вызова Method.invoke в пользовательском коде.
Генерация целевых делегатов с высокой производительностью; предпочтительна для библиотек и фреймворков, где требуется скорость.
Аналоги в других языках
Краткие отличия и примеры в популярных языках.
PHP - call_user_func_array
// PHP
function greet($name) { return "Hi, $name"; }
echo call_user_func_array('greet', ['Bob']);
Hi, Bob
JavaScript - Function.prototype.apply / Reflect.apply
// JavaScript
function sum(a, b) { return a + b; }
console.log(sum.apply(null, [1,2]));
// или
console.log(Reflect.apply(sum, null, [3,4]));
3 7
Python - getattr + вызов
# Python
class C:
def f(self, x):
return x*2
c = C()
func = getattr(c, 'f')
print(func(5))
10
C# - MethodInfo.Invoke
// C#
using System;
using System.Reflection;
class Program {
public static int Sum(int a,int b) => a+b;
static void Main(){
MethodInfo mi = typeof(Program).GetMethod("Sum", BindingFlags.Public | BindingFlags.Static);
Console.WriteLine(mi.Invoke(null, new object[]{2,3}));
}
}
5
Go - reflect.Value.Call
// Go
package main
import (
"fmt"
"reflect"
)
func Sum(a, b int) int { return a + b }
func main() {
fn := reflect.ValueOf(Sum)
args := []reflect.Value{reflect.ValueOf(2), reflect.ValueOf(3)}
res := fn.Call(args)
fmt.Println(res[0].Interface())
}
5
Kotlin - KFunction.call
// Kotlin
import kotlin.reflect.full.*
class A { fun g(x:Int)=x*3 }
fun main(){
val m = A::class.members.first{it.name=="g"}
println(m.call(A(), 3))
}
9
Lua - вызов функции по ссылке
-- Lua
function f(x) return x*2 end
local fn = f
print(fn(4))
8
SQL (пример динамическое выполнение в PostgreSQL)
-- PL/pgSQL
DO $$
BEGIN
EXECUTE 'SELECT 1+2';
END$$;
(выполняет динамическую SQL-команду, возвращаемое значение можно получить через INTO)
Отличия от Java:
- В большинстве скриптовых языков рефлексия проще и безопаснее по синтаксису, но не обеспечивает строгой типизации.
- В Go и C# рефлексия медленнее и более громоздкая, зато интегрирована с типовой системой.
- SQL делает динамический код на уровне БД и не сопоставим с вызовом методов объектов в JVM.
Типичные ошибки при использовании Method.invoke()
Список распространенных проблем с пояснениями и примерами.
InvocationTargetException оборачивает реальную ошибку
class T { void err() { throw new RuntimeException("oops"); } }
// вызов
Method m = T.class.getMethod("err");
try { m.invoke(new T()); }
catch (InvocationTargetException e) { System.out.println(e.getCause()); }
java.lang.RuntimeException: oops
IllegalAccessException из-за приватного метода и модульного ограничения
class S { private void p(){} }
Method m = S.class.getDeclaredMethod("p");
// без setAccessible(true) или при закрытом модуле вызовет исключение
m.invoke(new S());
java.lang.IllegalAccessException: class ... cannot access a member of class ...
IllegalArgumentException при несоответствии типов
class X { void a(int n){} }
Method m = X.class.getMethod("a", int.class);
m.invoke(new X(), "notInt");
java.lang.IllegalArgumentException: argument type mismatch
NullPointerException при вызове нестатического метода с null
class Y { void a(){} }
Method m = Y.class.getMethod("a");
m.invoke(null);
java.lang.NullPointerException
ClassCastException при неправильном приведении результата
Method m = String.class.getMethod("length");
Object r = m.invoke("abc");
Integer i = (Integer) r; // length возвращает int, упаковывается в Integer - это ОК
String s = (String) r; // неверно
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
Рекомендации по обработке ошибок: при InvocationTargetException всегда обращаться к getCause(); явно проверять типы аргументов и использовать isAssignableFrom для валидации.
Изменения, влияющие на рефлексию в последних версиях
Краткое описание важных эволюций и их влияния на Method.invoke().
- Java 7: активное развитие API
java.lang.invokeиMethodHandleкак более быстрый и гибкий механизм вызова. - Java 9: модульная система (JPMS) ввела строгую инкапсуляцию. Прямой рефлекторный доступ к приватным методам в других модулях может приводить к
IllegalAccessExceptionбез опций запуска (--add-opens) или использованияMethodHandles.privateLookupIn. - Java 11+ и далее: улучшения производительности JVM, оптимизации вызовов и invokedynamic; однако семантика
Method.invokeосталась прежней. Рекомендуется переходить наMethodHandleдля критичных по производительности задач.
Расширенные и необычные сценарии применения
Подробные примеры с пояснениями и результатами для реальных и редких случаев.
Кеширование Method и ускорение через MethodHandle
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
class Bench { public int inc(int x){ return x+1; } }
public class Adv1 {
public static void main(String[] args) throws Throwable {
Bench b = new Bench();
Method m = Bench.class.getMethod("inc", int.class);
// быстрый путь - MethodHandle
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle mh = lookup.findVirtual(Bench.class, "inc", MethodType.methodType(int.class, int.class));
// вызовы
long t0 = System.nanoTime();
for (int i=0;i<100000;i++) m.invoke(b, 1);
long t1 = System.nanoTime();
for (int i=0;i<100000;i++) mh.invokeExact(b, 1);
long t2 = System.nanoTime();
System.out.println("Method.invoke ms: " + (t1-t0)/1_000_000);
System.out.println("MethodHandle.invokeExact ms: " + (t2-t1)/1_000_000);
}
}
(примерный вывод - MethodHandle как правило быстрее, результаты зависят от JVM и оптимизаций)
Вызов метода varargs с массивом параметров
class V {
public String join(String... parts) { return String.join("-", parts); }
}
// вызов через рефлексию
Method m = V.class.getMethod("join", String[].class);
Object r = m.invoke(new V(), new Object[]{ new String[]{"a","b","c"} });
System.out.println(r);
a-b-c
Вызов default-метода интерфейса
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
interface I { default String d(){ return "def";} }
class C implements I {}
// вызов default через Method
Method m = I.class.getMethod("d");
m.setAccessible(true); // может не помочь в модульной среде
String r = (String) m.invoke(new C());
System.out.println(r);
def
Proxy и реализация InvocationHandler
import java.lang.reflect.*;
interface Hello { String hi(String name); }
class H implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("called: " + method.getName());
return "OK";
}
}
public class ProxyExample{
public static void main(String[] args){
Hello h = (Hello) Proxy.newProxyInstance(Hello.class.getClassLoader(), new Class[]{Hello.class}, new H());
System.out.println(h.hi("X"));
}
}
called: hi OK
Обработка приватных методов в модульной среде через MethodHandles.privateLookupIn
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
// предположительно класс в другом пакете/модуле с приватным методом
// MethodHandles.privateLookupIn(Target.class, MethodHandles.lookup())
// позволяет получить Lookup с правами для доступа к приватным методам (при наличии прав)
(пример требует тонкой настройки модулей и прав; в реальном коде возвращает MethodHandle или бросает исключение)
Каждый пример требует обработки исключений в реальном коде и учета модульных ограничений при работе с приватными элементами.