Back to Blog
Java

Java Invoke Method Reflection: A Practical Guide

java invoke method reflection: Learn how to invoke methods reflectively in Java: obtaining Method objects, handling parameters, access control, exceptions, and perform...

Java ReflectionMethod InvocationRuntime APIException HandlingJava Performance
A magnifying glass over a Java class structure with a method call arrow, representing reflective method invocation at runtime.

When you need to call a method whose name or signature is only known at runtime, the standard approach is java invoke method reflection via the java.lang.reflect.Method class. This is common in frameworks, serialization layers, and plugin systems where the target class cannot be referenced at compile time. The core sequence is always the same: obtain a Class object, locate the Method, make it accessible if necessary, and call invoke with a target instance and arguments.

Locating the Method Object

The first step is to retrieve a Method instance from a Class. The getMethod method returns public methods, including inherited ones, while getDeclaredMethod returns any method declared directly in the class, regardless of visibility. Both require the exact method name and parameter types.

Class<?> clazz = UserService.class; Method findUser = clazz.getMethod("findUser", String.class);

The parameter types are mandatory because Java allows method overloading. If you pass an incorrect type list, the lookup fails with a NoSuchMethodException. For a method with no parameters, pass an empty Class<?>[] or null.

For non-public methods, getDeclaredMethod is necessary. A common mistake is using getMethod for a private or protected method, which throws NoSuchMethodException even though the method exists.

Invoking the Method

Once you have a Method object, call invoke with the target instance and the actual arguments. For a static method, the instance argument is ignored and can be null.

UserService service = new UserService(); User user = (User) findUser.invoke(service, "alice");

The return value is always an Object. If the method returns a primitive, it is wrapped in its boxed type, so an int return becomes an Integer. If the method is void, invoke returns null. You must cast the result to the expected type, and this cast can fail at runtime if the actual return type differs from what you assumed.

Handling the Three Reflection Exceptions

Reflective invocation throws three checked exceptions, and each has a distinct meaning. The compiler forces you to handle them, but catching them blindly hides real bugs.

  • IllegalAccessException indicates the method is not accessible from the calling code. This happens when the method is private or package-private and the caller is in a different package.
  • InvocationTargetException wraps any exception thrown by the method itself. The actual failure is available via getCause().
  • IllegalArgumentException occurs when the target instance is not an instance of the declaring class, or when the argument types do not match the method signature.
try { Object result = findUser.invoke(service, "alice"); } catch (InvocationTargetException e) { Throwable cause = e.getCause(); // The method itself threw cause; handle it directly } catch (IllegalAccessException e) { // The method is not accessible; consider setAccessible(true) }

The most important detail is that InvocationTargetException is not the same as the original exception. If the invoked method throws a NullPointerException, you only see the wrapper unless you call getCause(). Failing to unwrap the cause makes debugging significantly harder because the stack trace points to the reflection call site rather than the actual failure point inside the method.

Accessing Private Methods with setAccessible

To invoke a private method, you must call setAccessible(true) on the Method object before invoking it. This suppresses the Java language access checks for that particular method instance.

Method privateMethod = clazz.getDeclaredMethod("calculateInternal", int.class); privateMethod.setAccessible(true); Object result = privateMethod.invoke(service, 42);

This works but has important consequences. In a modular application, the module system may still block access if the package is not exported to the calling module. In that case, setAccessible(true) throws InaccessibleObjectException, which is a runtime exception. Also, changing accessibility on a method that is part of a public API can break encapsulation guarantees and should be treated as a deliberate design decision, not a default technique.

Performance Cost and Caching Strategy

Reflective invocation is slower than a direct call because the JVM must perform dynamic dispatch, box arguments, and check types at runtime. The exact cost varies by JVM and workload, but the overhead is significant enough that you should avoid repeated lookups in hot paths.

// Avoid this pattern in a loop: for (String name : names) { Method m = clazz.getMethod("process", String.class); m.invoke(service, name); }

Instead, resolve the Method once and reuse it. The JVM can apply additional optimizations when the same Method object is invoked repeatedly, including the possibility of inlining in some JIT implementations.

Method process = clazz.getMethod("process", String.class); for (String name : names) { process.invoke(service, name); }

If you need to invoke many different methods dynamically, consider using java.lang.invoke.MethodHandle as a lower-level alternative. Method handles perform better in many cases and provide a more direct mapping to JVM invocation semantics, but they require more careful setup and are not always necessary for application-level code.

Choosing Between Reflection and Direct Calls

Reflection is the right tool when the method is genuinely unknown at compile time, such as when loading a class by name from a configuration file or implementing a generic event dispatcher. It is the wrong tool when you could use an interface, a lambda, or a method reference instead.

ApproachType SafetyRuntime CostUse Case
Direct callCompile-timeMinimalKnown method at compile time
Interface dispatchCompile-timeLowPolymorphic behavior
ReflectionRuntimeHigherDynamic method discovery
MethodHandleRuntimeModerateHigh-frequency dynamic calls

A practical rule: if you can express the behavior with an interface or a Consumer/Function, do that. Reflection should be reserved for cases where the method signature itself is part of the runtime data, such as a rule engine that maps string names to methods.

Compatibility and Maintainability Concerns

Reflection code is fragile in ways that direct code is not. A method rename or signature change will not cause a compile error; it will only fail at runtime when the lookup occurs. This shifts errors from build time to production time, which is a real operational cost.

To mitigate this, centralize all reflective lookups in a small number of dedicated classes rather than scattering them across the codebase. Write a unit test that invokes each reflected method at least once so that signature mismatches are caught early. Also, be explicit about which classes and methods are expected to be reflectively accessed, because the Java module system requires opens directives for deep reflection into non-exported packages.

When running under a SecurityManager (deprecated but still present in some environments), setAccessible(true) can throw SecurityException. In modern Java versions with modules, the more common failure is InaccessibleObjectException. Both are runtime exceptions, so they can bypass your checked exception handling and surface as unhandled failures. Check the module descriptors and the Java version you target to know which failure mode applies.

java invoke method reflection: Practical Usage and Code Exam | RYUSLOG DEV