Introduction to Reflection
Reflection in Java allows programs to inspect and manipulate classes, methods, fields, and constructors at runtime. It provides the ability to examine class structure and dynamically invoke methods without knowing them at compile time.
Key Reflection Classes
java.lang.Class: Represents classes and interfacesjava.lang.reflect.Method: Represents class methodsjava.lang.reflect.Field: Represents class fieldsjava.lang.reflect.Constructor: Represents class constructors
1. Basic Method Invocation
Getting Class Objects
public class ReflectionBasics {
// Sample class for demonstration
public static class Calculator {
public int add(int a, int b) {
return a + b;
}
public static double multiply(double a, double b) {
return a * b;
}
private String concatenate(String s1, String s2) {
return s1 + s2;
}
}
public static void main(String[] args) throws Exception {
// Different ways to get Class object
Class<?> clazz1 = Calculator.class;
Class<?> clazz2 = Class.forName("ReflectionBasics$Calculator");
Class<?> clazz3 = new Calculator().getClass();
System.out.println("Class name: " + clazz1.getName());
System.out.println("Simple name: " + clazz1.getSimpleName());
System.out.println("All classes are equal: " +
(clazz1 == clazz2 && clazz2 == clazz3));
}
}
Basic Method Invocation Example
import java.lang.reflect.Method;
public class BasicMethodInvocation {
public static class MathOperations {
public int add(int a, int b) {
System.out.println("Adding: " + a + " + " + b);
return a + b;
}
public String greet(String name) {
return "Hello, " + name + "!";
}
public static boolean isEven(int number) {
return number % 2 == 0;
}
}
public static void main(String[] args) throws Exception {
MathOperations obj = new MathOperations();
Class<?> clazz = obj.getClass();
// Get and invoke instance method
Method addMethod = clazz.getMethod("add", int.class, int.class);
Object result1 = addMethod.invoke(obj, 10, 20);
System.out.println("Result: " + result1);
// Get and invoke instance method with different signature
Method greetMethod = clazz.getMethod("greet", String.class);
Object result2 = greetMethod.invoke(obj, "Alice");
System.out.println("Result: " + result2);
// Get and invoke static method
Method isEvenMethod = clazz.getMethod("isEven", int.class);
Object result3 = isEvenMethod.invoke(null, 15); // null for static methods
System.out.println("Is 15 even? " + result3);
}
}
2. Advanced Method Discovery
Finding and Filtering Methods
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
public class MethodDiscovery {
public static class ComplexClass {
public void publicMethod() {}
protected void protectedMethod() {}
private void privateMethod() {}
static void staticMethod() {}
final void finalMethod() {}
public void overloadedMethod() {}
public void overloadedMethod(String param) {}
public void overloadedMethod(int param) {}
public String methodWithReturn() { return "result"; }
public void methodWithParams(String s, int i, boolean b) {}
}
public static void main(String[] args) {
Class<?> clazz = ComplexClass.class;
// Get all public methods (including inherited)
System.out.println("=== ALL PUBLIC METHODS ===");
Method[] allPublicMethods = clazz.getMethods();
for (Method method : allPublicMethods) {
System.out.println(method.getName() + " - " +
Arrays.toString(method.getParameterTypes()));
}
// Get all declared methods (only from this class)
System.out.println("\n=== ALL DECLARED METHODS ===");
Method[] allDeclaredMethods = clazz.getDeclaredMethods();
for (Method method : allDeclaredMethods) {
System.out.println(method.getName() + " - " +
Modifier.toString(method.getModifiers()));
}
// Find methods by name and parameters
System.out.println("\n=== FIND SPECIFIC METHODS ===");
try {
Method noParamMethod = clazz.getMethod("overloadedMethod");
System.out.println("Found: " + noParamMethod);
Method stringParamMethod = clazz.getMethod("overloadedMethod", String.class);
System.out.println("Found: " + stringParamMethod);
Method intParamMethod = clazz.getMethod("overloadedMethod", int.class);
System.out.println("Found: " + intParamMethod);
} catch (NoSuchMethodException e) {
System.out.println("Method not found: " + e.getMessage());
}
// Filter methods by modifiers
System.out.println("\n=== STATIC METHODS ===");
for (Method method : allDeclaredMethods) {
if (Modifier.isStatic(method.getModifiers())) {
System.out.println(method.getName());
}
}
System.out.println("\n=== PRIVATE METHODS ===");
for (Method method : allDeclaredMethods) {
if (Modifier.isPrivate(method.getModifiers())) {
System.out.println(method.getName());
}
}
}
}
Method Information Inspection
import java.lang.reflect.Method;
import java.lang.reflect.Type;
public class MethodInspection {
public static class InspectionTarget {
public static final String CONSTANT = "TEST";
public <T> T genericMethod(T input, Class<T> clazz) {
return input;
}
public String methodWithExceptions() throws IllegalArgumentException,
IllegalStateException {
return "test";
}
@Deprecated
public void oldMethod() {
System.out.println("This method is deprecated");
}
}
public static void main(String[] args) throws Exception {
Class<?> clazz = InspectionTarget.class;
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
System.out.println("\n=== METHOD: " + method.getName() + " ===");
// Basic information
System.out.println("Name: " + method.getName());
System.out.println("Return Type: " + method.getReturnType());
System.out.println("Generic Return Type: " + method.getGenericReturnType());
System.out.println("Modifiers: " + Modifier.toString(method.getModifiers()));
// Parameters
Class<?>[] paramTypes = method.getParameterTypes();
Type[] genericParamTypes = method.getGenericParameterTypes();
System.out.println("Parameter count: " + paramTypes.length);
for (int i = 0; i < paramTypes.length; i++) {
System.out.println(" Param " + i + ": " + paramTypes[i] +
" (Generic: " + genericParamTypes[i] + ")");
}
// Exceptions
Class<?>[] exceptionTypes = method.getExceptionTypes();
System.out.println("Exception count: " + exceptionTypes.length);
for (Class<?> exType : exceptionTypes) {
System.out.println(" Throws: " + exType.getName());
}
// Annotations
java.lang.annotation.Annotation[] annotations = method.getAnnotations();
System.out.println("Annotation count: " + annotations.length);
for (java.lang.annotation.Annotation annotation : annotations) {
System.out.println(" @" + annotation.annotationType().getSimpleName());
}
// Method signature
System.out.println("Signature: " + method.toGenericString());
}
}
}
3. Advanced Invocation Techniques
Invoking Private Methods
import java.lang.reflect.Method;
public class PrivateMethodInvocation {
public static class SecureClass {
private String secretKey = "TOP_SECRET_123";
private String getSecretData() {
return "Confidential information: " + secretKey;
}
private int calculateHash(String input) {
return input.hashCode();
}
private static String staticPrivateMethod() {
return "Static private method result";
}
}
public static void main(String[] args) throws Exception {
SecureClass obj = new SecureClass();
Class<?> clazz = obj.getClass();
// Get private instance method
Method getSecretDataMethod = clazz.getDeclaredMethod("getSecretData");
getSecretDataMethod.setAccessible(true); // Break encapsulation!
Object result1 = getSecretDataMethod.invoke(obj);
System.out.println("Secret data: " + result1);
// Get private instance method with parameters
Method calculateHashMethod = clazz.getDeclaredMethod("calculateHash", String.class);
calculateHashMethod.setAccessible(true);
Object result2 = calculateHashMethod.invoke(obj, "test input");
System.out.println("Hash: " + result2);
// Get private static method
Method staticPrivateMethod = clazz.getDeclaredMethod("staticPrivateMethod");
staticPrivateMethod.setAccessible(true);
Object result3 = staticPrivateMethod.invoke(null); // null for static
System.out.println("Static result: " + result3);
// Check accessibility status
System.out.println("Is accessible: " + getSecretDataMethod.isAccessible());
// Restore accessibility (good practice)
getSecretDataMethod.setAccessible(false);
}
}
Handling Generic Methods
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
public class GenericMethodInvocation {
public static class GenericProcessor {
public <T> T processItem(T item) {
System.out.println("Processing: " + item + " (" + item.getClass() + ")");
return item;
}
public <T> List<T> createList(T... items) {
List<T> list = new ArrayList<>();
for (T item : items) {
list.add(item);
}
return list;
}
public <K, V> String createPair(K key, V value) {
return key + " -> " + value;
}
}
public static void main(String[] args) throws Exception {
GenericProcessor processor = new GenericProcessor();
Class<?> clazz = processor.getClass();
// Invoke simple generic method
Method processItemMethod = clazz.getMethod("processItem", Object.class);
// Type erasure - we can pass any type at runtime
Object stringResult = processItemMethod.invoke(processor, "Hello World");
System.out.println("String result: " + stringResult);
Object intResult = processItemMethod.invoke(processor, 42);
System.out.println("Int result: " + intResult);
// Invoke varargs generic method
Method createListMethod = clazz.getMethod("createList", Object[].class);
// For varargs, we need to wrap arguments in Object[]
Object[] stringArgs = new Object[]{"A", "B", "C"};
Object listResult = createListMethod.invoke(processor, (Object) stringArgs);
System.out.println("List result: " + listResult);
System.out.println("List type: " + listResult.getClass());
System.out.println("List contents: " + listResult);
// Invoke multiple type parameters method
Method createPairMethod = clazz.getMethod("createPair", Object.class, Object.class);
Object pairResult = createPairMethod.invoke(processor, "name", "Alice");
System.out.println("Pair result: " + pairResult);
}
}
4. Performance Considerations and Caching
Method Caching for Performance
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class OptimizedReflection {
public static class DataProcessor {
public String processText(String text) {
return text.toUpperCase();
}
public int processNumber(int number) {
return number * 2;
}
public boolean validateInput(String input, int maxLength) {
return input != null && input.length() <= maxLength;
}
}
// Method cache for better performance
private static final ConcurrentMap<String, Method> METHOD_CACHE = new ConcurrentHashMap<>();
public static Method getCachedMethod(Class<?> clazz, String methodName, Class<?>... paramTypes)
throws NoSuchMethodException {
String cacheKey = generateCacheKey(clazz, methodName, paramTypes);
return METHOD_CACHE.computeIfAbsent(cacheKey, key -> {
try {
return clazz.getMethod(methodName, paramTypes);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
});
}
private static String generateCacheKey(Class<?> clazz, String methodName, Class<?>... paramTypes) {
StringBuilder key = new StringBuilder();
key.append(clazz.getName()).append(".").append(methodName);
for (Class<?> paramType : paramTypes) {
key.append(":").append(paramType.getName());
}
return key.toString();
}
public static void main(String[] args) throws Exception {
DataProcessor processor = new DataProcessor();
// First call - loads into cache
long startTime = System.nanoTime();
Method method1 = getCachedMethod(DataProcessor.class, "processText", String.class);
Object result1 = method1.invoke(processor, "hello");
long time1 = System.nanoTime() - startTime;
// Second call - uses cached method
startTime = System.nanoTime();
Method method2 = getCachedMethod(DataProcessor.class, "processText", String.class);
Object result2 = method2.invoke(processor, "world");
long time2 = System.nanoTime() - startTime;
System.out.println("First invocation: " + time1 + " ns");
System.out.println("Second invocation: " + time2 + " ns");
System.out.println("Performance improvement: " + (time1 - time2) + " ns");
System.out.println("Methods are same: " + (method1 == method2));
// Benchmark multiple invocations
benchmarkReflection(processor);
}
private static void benchmarkReflection(DataProcessor processor) throws Exception {
int iterations = 10000;
// Direct invocation
long directStart = System.nanoTime();
for (int i = 0; i < iterations; i++) {
processor.processText("test");
}
long directTime = System.nanoTime() - directStart;
// Cached reflection
Method cachedMethod = getCachedMethod(DataProcessor.class, "processText", String.class);
long cachedStart = System.nanoTime();
for (int i = 0; i < iterations; i++) {
cachedMethod.invoke(processor, "test");
}
long cachedTime = System.nanoTime() - cachedStart;
// Uncached reflection
long uncachedStart = System.nanoTime();
for (int i = 0; i < iterations; i++) {
Method method = DataProcessor.class.getMethod("processText", String.class);
method.invoke(processor, "test");
}
long uncachedTime = System.nanoTime() - uncachedStart;
System.out.println("\n=== PERFORMANCE COMPARISON ===");
System.out.println("Direct invocation: " + directTime / iterations + " ns/call");
System.out.println("Cached reflection: " + cachedTime / iterations + " ns/call");
System.out.println("Uncached reflection: " + uncachedTime / iterations + " ns/call");
System.out.println("Cached vs Direct overhead: " +
(cachedTime - directTime) * 100.0 / directTime + "%");
}
}
5. Error Handling and Robust Invocation
Comprehensive Error Handling
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
public class RobustMethodInvocation {
public static class ProblematicClass {
public void normalMethod(String input) {
System.out.println("Normal: " + input);
}
public void exceptionMethod() {
throw new IllegalStateException("This method always fails");
}
public void nullPointerMethod(String input) {
if (input == null) {
throw new NullPointerException("Input cannot be null");
}
System.out.println("Processed: " + input);
}
private void privateMethod() {
System.out.println("This is private");
}
}
public static Object safeInvoke(Object obj, String methodName, Object... args) {
try {
Class<?> clazz = obj.getClass();
// Convert arguments to parameter types
Class<?>[] paramTypes = new Class<?>[args.length];
for (int i = 0; i < args.length; i++) {
paramTypes[i] = args[i] != null ? args[i].getClass() : Object.class;
}
// Find the method
Method method = findBestMethod(clazz, methodName, paramTypes);
if (method == null) {
return "Method not found: " + methodName +
" with parameters " + Arrays.toString(paramTypes);
}
// Ensure accessibility
boolean wasAccessible = method.isAccessible();
if (!wasAccessible) {
method.setAccessible(true);
}
try {
// Invoke the method
return method.invoke(obj, args);
} finally {
// Restore accessibility
if (!wasAccessible) {
method.setAccessible(false);
}
}
} catch (IllegalAccessException e) {
return "Access denied for method: " + methodName + " - " + e.getMessage();
} catch (IllegalArgumentException e) {
return "Invalid arguments for method: " + methodName + " - " + e.getMessage();
} catch (InvocationTargetException e) {
// This wraps the actual exception thrown by the method
Throwable targetException = e.getTargetException();
return "Method threw exception: " + targetException.getClass().getSimpleName() +
" - " + targetException.getMessage();
} catch (Exception e) {
return "Unexpected error: " + e.getClass().getSimpleName() + " - " + e.getMessage();
}
}
private static Method findBestMethod(Class<?> clazz, String methodName, Class<?>[] paramTypes) {
try {
// First try exact match
return clazz.getMethod(methodName, paramTypes);
} catch (NoSuchMethodException e) {
// Try declared methods
try {
return clazz.getDeclaredMethod(methodName, paramTypes);
} catch (NoSuchMethodException e2) {
// Fall back to searching through all methods
for (Method method : clazz.getMethods()) {
if (method.getName().equals(methodName) &&
method.getParameterCount() == paramTypes.length) {
// Simple parameter type compatibility check
if (isCompatible(method.getParameterTypes(), paramTypes)) {
return method;
}
}
}
return null;
}
}
}
private static boolean isCompatible(Class<?>[] expected, Class<?>[] actual) {
if (expected.length != actual.length) return false;
for (int i = 0; i < expected.length; i++) {
if (!expected[i].isAssignableFrom(actual[i]) &&
!isBoxingCompatible(expected[i], actual[i])) {
return false;
}
}
return true;
}
private static boolean isBoxingCompatible(Class<?> expected, Class<?> actual) {
// Handle primitive/boxed type compatibility
return (expected.isPrimitive() && getWrapperClass(expected) == actual) ||
(actual.isPrimitive() && getWrapperClass(actual) == expected);
}
private static Class<?> getWrapperClass(Class<?> primitive) {
if (primitive == int.class) return Integer.class;
if (primitive == long.class) return Long.class;
if (primitive == double.class) return Double.class;
if (primitive == float.class) return Float.class;
if (primitive == boolean.class) return Boolean.class;
if (primitive == byte.class) return Byte.class;
if (primitive == char.class) return Character.class;
if (primitive == short.class) return Short.class;
return primitive;
}
public static void main(String[] args) {
ProblematicClass obj = new ProblematicClass();
// Test various scenarios
System.out.println("1. Normal method: " + safeInvoke(obj, "normalMethod", "Hello"));
System.out.println("2. Exception method: " + safeInvoke(obj, "exceptionMethod"));
System.out.println("3. Null pointer: " + safeInvoke(obj, "nullPointerMethod", (String) null));
System.out.println("4. Private method: " + safeInvoke(obj, "privateMethod"));
System.out.println("5. Non-existent method: " + safeInvoke(obj, "nonExistentMethod"));
System.out.println("6. Wrong parameters: " + safeInvoke(obj, "normalMethod", 123));
}
}
6. Real-World Use Cases
Plugin System with Reflection
import java.lang.reflect.Method;
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.ServiceLoader;
// Plugin interface
interface Plugin {
String getName();
void initialize();
String execute(String input);
void cleanup();
}
// Plugin manager using reflection
public class PluginSystem {
private List<Plugin> plugins = new ArrayList<>();
public void loadPlugins(String pluginDir) throws Exception {
File dir = new File(pluginDir);
if (!dir.exists() || !dir.isDirectory()) {
System.out.println("Plugin directory not found: " + pluginDir);
return;
}
File[] jarFiles = dir.listFiles((d, name) -> name.endsWith(".jar"));
if (jarFiles == null) return;
for (File jarFile : jarFiles) {
try {
loadPluginFromJar(jarFile);
} catch (Exception e) {
System.err.println("Failed to load plugin from " + jarFile + ": " + e.getMessage());
}
}
}
private void loadPluginFromJar(File jarFile) throws Exception {
URL jarUrl = jarFile.toURI().toURL();
URLClassLoader classLoader = new URLClassLoader(new URL[]{jarUrl});
// Look for plugin implementation classes
// In real implementation, you might use service loader or configuration
String[] candidateClasses = findPluginClasses(jarFile);
for (String className : candidateClasses) {
try {
Class<?> clazz = classLoader.loadClass(className);
// Check if it implements Plugin interface
if (Plugin.class.isAssignableFrom(clazz)) {
Plugin plugin = (Plugin) clazz.getDeclaredConstructor().newInstance();
plugins.add(plugin);
System.out.println("Loaded plugin: " + plugin.getName());
}
} catch (Exception e) {
System.err.println("Failed to load class " + className + ": " + e.getMessage());
}
}
}
private String[] findPluginClasses(File jarFile) {
// Simplified - in real implementation, scan JAR manifest or annotations
return new String[]{
"com.example.plugins.TextProcessorPlugin",
"com.example.plugins.DataTransformerPlugin"
};
}
public void executeAll(String input) {
for (Plugin plugin : plugins) {
try {
plugin.initialize();
String result = plugin.execute(input);
System.out.println(plugin.getName() + " result: " + result);
plugin.cleanup();
} catch (Exception e) {
System.err.println("Plugin " + plugin.getName() + " failed: " + e.getMessage());
}
}
}
// Dynamic method invocation on plugins
public void invokePluginMethod(String pluginName, String methodName, Object... args) {
for (Plugin plugin : plugins) {
if (plugin.getName().equals(pluginName)) {
try {
Method method = findMethod(plugin, methodName, args);
if (method != null) {
Object result = method.invoke(plugin, args);
System.out.println("Method " + methodName + " result: " + result);
} else {
System.err.println("Method not found: " + methodName);
}
} catch (Exception e) {
System.err.println("Failed to invoke method: " + e.getMessage());
}
return;
}
}
System.err.println("Plugin not found: " + pluginName);
}
private Method findMethod(Object obj, String methodName, Object[] args) {
Class<?> clazz = obj.getClass();
for (Method method : clazz.getMethods()) {
if (method.getName().equals(methodName) &&
method.getParameterCount() == args.length) {
return method;
}
}
return null;
}
}
Dynamic Service Invoker
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class DynamicServiceInvoker {
private Map<String, Object> services = new HashMap<>();
private Map<String, Method> methodCache = new HashMap<>();
public void registerService(String name, Object service) {
services.put(name, service);
cacheServiceMethods(service);
}
private void cacheServiceMethods(Object service) {
Class<?> clazz = service.getClass();
for (Method method : clazz.getMethods()) {
String cacheKey = clazz.getSimpleName() + "." + method.getName();
methodCache.put(cacheKey, method);
}
}
public Object invokeService(String serviceName, String methodName, Object... args) {
Object service = services.get(serviceName);
if (service == null) {
throw new IllegalArgumentException("Service not found: " + serviceName);
}
String cacheKey = service.getClass().getSimpleName() + "." + methodName;
Method method = methodCache.get(cacheKey);
if (method == null) {
throw new IllegalArgumentException("Method not found: " + methodName);
}
try {
return method.invoke(service, args);
} catch (Exception e) {
throw new RuntimeException("Failed to invoke service method", e);
}
}
// Example usage
public static class UserService {
public String getUserInfo(String userId) {
return "User info for: " + userId;
}
public boolean validateUser(String username, String password) {
return "admin".equals(username) && "secret".equals(password);
}
}
public static class OrderService {
public String createOrder(String product, int quantity) {
return "Order created: " + quantity + " x " + product;
}
}
public static void main(String[] args) {
DynamicServiceInvoker invoker = new DynamicServiceInvoker();
// Register services
invoker.registerService("userService", new UserService());
invoker.registerService("orderService", new OrderService());
// Invoke methods dynamically
Object result1 = invoker.invokeService("userService", "getUserInfo", "user123");
System.out.println("Result 1: " + result1);
Object result2 = invoker.invokeService("userService", "validateUser", "admin", "secret");
System.out.println("Result 2: " + result2);
Object result3 = invoker.invokeService("orderService", "createOrder", "Laptop", 2);
System.out.println("Result 3: " + result3);
}
}
7. Best Practices and Performance
Performance Optimization Tips
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class ReflectionBestPractices {
// 1. Cache Method objects
private static final ConcurrentMap<String, Method> METHOD_CACHE = new ConcurrentHashMap<>();
// 2. Use setAccessible wisely (cache the accessibility change)
private static final ConcurrentMap<Method, Boolean> ACCESSIBILITY_CACHE = new ConcurrentHashMap<>();
public static Method getCachedMethod(Class<?> clazz, String methodName, Class<?>... paramTypes) {
String key = clazz.getName() + "." + methodName + java.util.Arrays.toString(paramTypes);
return METHOD_CACHE.computeIfAbsent(key, k -> {
try {
Method method = clazz.getMethod(methodName, paramTypes);
// Pre-set accessibility for frequently used methods
if (needsAccessibility(method)) {
method.setAccessible(true);
ACCESSIBILITY_CACHE.put(method, true);
}
return method;
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
});
}
private static boolean needsAccessibility(Method method) {
// Your logic to determine if method needs accessibility override
return !method.isAccessible() && method.getName().startsWith("private");
}
// 3. Use MethodHandle for better performance (Java 7+)
public static class MethodHandleExample {
/*
private static final Map<String, MethodHandle> HANDLE_CACHE = new ConcurrentHashMap<>();
public static MethodHandle getMethodHandle(Class<?> clazz, String methodName, Class<?>... paramTypes) {
String key = clazz.getName() + "." + methodName + Arrays.toString(paramTypes);
return HANDLE_CACHE.computeIfAbsent(key, k -> {
try {
MethodHandles.Lookup lookup = MethodHandles.lookup();
Method method = clazz.getMethod(methodName, paramTypes);
return lookup.unreflect(method);
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
*/
}
// 4. Avoid reflection in performance-critical loops
public static class OptimizedInvocation {
private final Object target;
private final Method method;
public OptimizedInvocation(Object target, String methodName, Class<?>... paramTypes) {
this.target = target;
try {
this.method = target.getClass().getMethod(methodName, paramTypes);
this.method.setAccessible(true);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
public Object invoke(Object... args) {
try {
return method.invoke(target, args);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
// Security considerations
class SecureReflection {
public static void checkAccessPermission(Method method) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
// Check if caller has permission to access this method
sm.checkPermission(new java.lang.reflect.ReflectPermission("suppressAccessChecks"));
}
}
public static Object secureInvoke(Method method, Object obj, Object... args) throws Exception {
checkAccessPermission(method);
// Additional security checks
if (method.getName().contains("secret") || method.getName().contains("password")) {
throw new SecurityException("Access to sensitive method denied");
}
return method.invoke(obj, args);
}
}
Summary
Key Points:
- Basic Invocation: Use
Method.invoke()with proper parameter handling - Performance: Cache Method objects and consider MethodHandle for critical paths
- Error Handling: Always handle
InvocationTargetExceptionand other reflection exceptions - Security: Be cautious with
setAccessible(true)and validate method access - Generics: Remember type erasure when working with generic methods
Common Use Cases:
- Plugin systems and extensible architectures
- Framework development (Spring, Hibernate)
- Testing and mocking frameworks
- Dynamic service invocation
- Serialization/deserialization libraries
Performance Comparison:
- Direct invocation: Fastest (baseline)
- Cached reflection: 2-10x slower than direct
- Uncached reflection: 10-100x slower than direct
- MethodHandle: Similar to cached reflection, sometimes faster
Reflection is a powerful tool but should be used judiciously due to its performance overhead and security implications. Always prefer direct invocation when possible and use reflection only for truly dynamic scenarios.