1. java
  2. /advanced
  3. /reflection

Master Java Reflection and Runtime Introspection

Java Reflection

Java Reflection is a powerful feature that allows programs to examine and manipulate their own structure at runtime. It provides the ability to inspect classes, methods, fields, and constructors, and even invoke methods or modify field values dynamically. While powerful, reflection should be used judiciously due to performance implications and security considerations.

What is Reflection?

Reflection is the ability of a program to examine its own structure and behavior at runtime. In Java, reflection is implemented through the java.lang.reflect package, which provides classes and interfaces to obtain information about classes, methods, fields, and constructors.

Key Capabilities:

  • Class Inspection: Examine class structure, hierarchy, and metadata
  • Method Invocation: Call methods dynamically by name
  • Field Access: Read and modify field values at runtime
  • Constructor Access: Create instances using constructors dynamically
  • Annotation Reading: Access annotation metadata at runtime

Why Reflection Matters:

  1. Framework Development: Essential for frameworks like Spring, Hibernate, JUnit
  2. Serialization: Converting objects to/from JSON, XML, or binary formats
  3. Dependency Injection: Automatically wiring object dependencies
  4. Testing: Accessing private methods and fields for unit testing
  5. Configuration: Dynamic configuration and object creation
// Without reflection (static approach)
public class StaticExample {
    public static void createUser() {
        User user = new User(); // Fixed class at compile time
        user.setName("John");   // Fixed method at compile time
        user.setEmail("[email protected]");
    }
}

// With reflection (dynamic approach)
public class ReflectionExample {
    public static void createObjectDynamically(String className) throws Exception {
        Class<?> clazz = Class.forName(className); // Dynamic class loading
        Object instance = clazz.getDeclaredConstructor().newInstance(); // Dynamic instantiation
        
        Method setName = clazz.getMethod("setName", String.class); // Dynamic method lookup
        setName.invoke(instance, "John"); // Dynamic method invocation
        
        Method setEmail = clazz.getMethod("setEmail", String.class);
        setEmail.invoke(instance, "[email protected]");
        
        System.out.println("Created: " + instance);
    }
}

Getting Class Objects

The Class object is the entry point for reflection operations:

import java.lang.reflect.*;
import java.util.*;

public class ClassObjectExample {
    
    public static void demonstrateClassObjects() {
        // Different ways to obtain Class objects
        
        // 1. Using .class literal (compile-time)
        Class<String> stringClass = String.class;
        Class<List> listClass = List.class;
        
        // 2. Using getClass() method (runtime)
        String str = "Hello";
        Class<?> stringClass2 = str.getClass();
        
        // 3. Using Class.forName() (dynamic loading)
        try {
            Class<?> stringClass3 = Class.forName("java.lang.String");
            Class<?> arrayListClass = Class.forName("java.util.ArrayList");
            
            // For arrays
            Class<?> intArrayClass = Class.forName("[I"); // int[]
            Class<?> stringArrayClass = Class.forName("[Ljava.lang.String;"); // String[]
            
            System.out.println("All String class objects equal: " + 
                (stringClass == stringClass2 && stringClass2 == stringClass3));
                
        } catch (ClassNotFoundException e) {
            System.err.println("Class not found: " + e.getMessage());
        }
        
        // 4. For primitive types
        Class<Integer> intWrapperClass = Integer.class;
        Class<?> intPrimitiveClass = int.class;
        Class<?> intPrimitiveClass2 = Integer.TYPE;
        
        System.out.println("int.class == Integer.TYPE: " + (intPrimitiveClass == intPrimitiveClass2));
        System.out.println("int.class != Integer.class: " + (intPrimitiveClass != intWrapperClass));
    }
    
    public static void exploreClassInformation() {
        Class<ArrayList> clazz = ArrayList.class;
        
        // Basic class information
        System.out.println("=== Class Information ===");
        System.out.println("Simple name: " + clazz.getSimpleName());
        System.out.println("Canonical name: " + clazz.getCanonicalName());
        System.out.println("Package: " + clazz.getPackage().getName());
        System.out.println("Modifiers: " + Modifier.toString(clazz.getModifiers()));
        
        // Hierarchy information
        System.out.println("\n=== Class Hierarchy ===");
        System.out.println("Superclass: " + clazz.getSuperclass().getSimpleName());
        
        System.out.println("Interfaces:");
        for (Class<?> iface : clazz.getInterfaces()) {
            System.out.println("  - " + iface.getSimpleName());
        }
        
        // Generic type information
        System.out.println("\n=== Generic Information ===");
        TypeVariable<?>[] typeParams = clazz.getTypeParameters();
        if (typeParams.length > 0) {
            System.out.println("Type parameters:");
            for (TypeVariable<?> typeParam : typeParams) {
                System.out.println("  - " + typeParam.getName());
            }
        }
    }
    
    public static void main(String[] args) {
        demonstrateClassObjects();
        System.out.println();
        exploreClassInformation();
    }
}

Working with Fields

Reflection allows you to inspect and modify fields dynamically:

import java.lang.reflect.*;

public class FieldReflectionExample {
    
    public static class Person {
        public String name;
        protected int age;
        private String email;
        private static final String SPECIES = "Homo sapiens";
        
        public Person(String name, int age, String email) {
            this.name = name;
            this.age = age;
            this.email = email;
        }
        
        @Override
        public String toString() {
            return "Person{name='" + name + "', age=" + age + ", email='" + email + "'}";
        }
    }
    
    public static void exploreFields() {
        Class<Person> clazz = Person.class;
        
        System.out.println("=== All Declared Fields ===");
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field field : declaredFields) {
            System.out.println("Field: " + field.getName());
            System.out.println("  Type: " + field.getType().getSimpleName());
            System.out.println("  Modifiers: " + Modifier.toString(field.getModifiers()));
            System.out.println("  Generic Type: " + field.getGenericType());
            System.out.println();
        }
        
        System.out.println("=== Public Fields Only ===");
        Field[] publicFields = clazz.getFields();
        for (Field field : publicFields) {
            System.out.println("Public field: " + field.getName());
        }
    }
    
    public static void accessAndModifyFields() {
        Person person = new Person("John Doe", 30, "[email protected]");
        Class<Person> clazz = Person.class;
        
        System.out.println("Original: " + person);
        
        try {
            // Access public field
            Field nameField = clazz.getField("name");
            String currentName = (String) nameField.get(person);
            System.out.println("Current name: " + currentName);
            
            nameField.set(person, "Jane Doe");
            System.out.println("After name change: " + person);
            
            // Access private field
            Field emailField = clazz.getDeclaredField("email");
            emailField.setAccessible(true); // Bypass access control
            
            String currentEmail = (String) emailField.get(person);
            System.out.println("Current email: " + currentEmail);
            
            emailField.set(person, "[email protected]");
            System.out.println("After email change: " + person);
            
            // Access static field
            Field speciesField = clazz.getDeclaredField("SPECIES");
            speciesField.setAccessible(true);
            String species = (String) speciesField.get(null); // null for static fields
            System.out.println("Species: " + species);
            
        } catch (NoSuchFieldException | IllegalAccessException e) {
            System.err.println("Field access error: " + e.getMessage());
        }
    }
    
    // Utility method to set any field value
    public static void setFieldValue(Object obj, String fieldName, Object value) {
        try {
            Class<?> clazz = obj.getClass();
            Field field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(obj, value);
        } catch (Exception e) {
            System.err.println("Failed to set field " + fieldName + ": " + e.getMessage());
        }
    }
    
    // Utility method to get any field value
    public static Object getFieldValue(Object obj, String fieldName) {
        try {
            Class<?> clazz = obj.getClass();
            Field field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);
            return field.get(obj);
        } catch (Exception e) {
            System.err.println("Failed to get field " + fieldName + ": " + e.getMessage());
            return null;
        }
    }
    
    public static void main(String[] args) {
        exploreFields();
        System.out.println();
        accessAndModifyFields();
        
        // Test utility methods
        System.out.println("\n=== Using Utility Methods ===");
        Person person = new Person("Test User", 25, "[email protected]");
        
        setFieldValue(person, "age", 35);
        int age = (Integer) getFieldValue(person, "age");
        System.out.println("Age after modification: " + age);
    }
}

Working with Methods

Method reflection allows dynamic method discovery and invocation:

import java.lang.reflect.*;
import java.util.*;

public class MethodReflectionExample {
    
    public static class Calculator {
        
        public int add(int a, int b) {
            return a + b;
        }
        
        public double multiply(double a, double b) {
            return a * b;
        }
        
        private String formatResult(double result) {
            return String.format("%.2f", result);
        }
        
        public static String getVersion() {
            return "Calculator v1.0";
        }
        
        // Method with annotations
        @Deprecated
        public int oldAdd(int a, int b) {
            return a + b;
        }
        
        // Varargs method
        public int sum(int... numbers) {
            return Arrays.stream(numbers).sum();
        }
        
        // Generic method
        public <T> List<T> createList(T... items) {
            return Arrays.asList(items);
        }
    }
    
    public static void exploreMethods() {
        Class<Calculator> clazz = Calculator.class;
        
        System.out.println("=== All Declared Methods ===");
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method method : declaredMethods) {
            System.out.println("Method: " + method.getName());
            System.out.println("  Return type: " + method.getReturnType().getSimpleName());
            System.out.println("  Modifiers: " + Modifier.toString(method.getModifiers()));
            
            // Parameter information
            Parameter[] parameters = method.getParameters();
            System.out.print("  Parameters: ");
            if (parameters.length == 0) {
                System.out.println("none");
            } else {
                for (int i = 0; i < parameters.length; i++) {
                    if (i > 0) System.out.print(", ");
                    System.out.print(parameters[i].getType().getSimpleName() + " " + 
                                   parameters[i].getName());
                }
                System.out.println();
            }
            
            // Exception information
            Class<?>[] exceptions = method.getExceptionTypes();
            if (exceptions.length > 0) {
                System.out.print("  Throws: ");
                for (int i = 0; i < exceptions.length; i++) {
                    if (i > 0) System.out.print(", ");
                    System.out.print(exceptions[i].getSimpleName());
                }
                System.out.println();
            }
            
            // Annotation information
            if (method.isAnnotationPresent(Deprecated.class)) {
                System.out.println("  ⚠️ Deprecated");
            }
            
            System.out.println();
        }
    }
    
    public static void invokeMethodsDynamically() {
        Calculator calc = new Calculator();
        Class<Calculator> clazz = Calculator.class;
        
        try {
            // Invoke simple method
            Method addMethod = clazz.getMethod("add", int.class, int.class);
            Integer result = (Integer) addMethod.invoke(calc, 5, 3);
            System.out.println("5 + 3 = " + result);
            
            // Invoke method with different parameter types
            Method multiplyMethod = clazz.getMethod("multiply", double.class, double.class);
            Double multiplyResult = (Double) multiplyMethod.invoke(calc, 4.5, 2.0);
            System.out.println("4.5 * 2.0 = " + multiplyResult);
            
            // Invoke private method
            Method formatMethod = clazz.getDeclaredMethod("formatResult", double.class);
            formatMethod.setAccessible(true);
            String formatted = (String) formatMethod.invoke(calc, 123.456);
            System.out.println("Formatted result: " + formatted);
            
            // Invoke static method
            Method versionMethod = clazz.getMethod("getVersion");
            String version = (String) versionMethod.invoke(null); // null for static methods
            System.out.println("Version: " + version);
            
            // Invoke varargs method
            Method sumMethod = clazz.getMethod("sum", int[].class);
            Integer sum = (Integer) sumMethod.invoke(calc, new int[]{1, 2, 3, 4, 5});
            System.out.println("Sum of 1,2,3,4,5 = " + sum);
            
        } catch (Exception e) {
            System.err.println("Method invocation error: " + e.getMessage());
            e.printStackTrace();
        }
    }
    
    // Dynamic method dispatcher
    public static Object callMethod(Object obj, String methodName, Object... args) {
        try {
            Class<?> clazz = obj.getClass();
            Method[] methods = clazz.getDeclaredMethods();
            
            // Find method by name and parameter count
            for (Method method : methods) {
                if (method.getName().equals(methodName) && 
                    method.getParameterCount() == args.length) {
                    
                    // Check if parameter types match
                    Class<?>[] paramTypes = method.getParameterTypes();
                    boolean matches = true;
                    
                    for (int i = 0; i < args.length; i++) {
                        if (args[i] != null && !isAssignable(paramTypes[i], args[i].getClass())) {
                            matches = false;
                            break;
                        }
                    }
                    
                    if (matches) {
                        method.setAccessible(true);
                        return method.invoke(obj, args);
                    }
                }
            }
            
            throw new NoSuchMethodException("No matching method found: " + methodName);
            
        } catch (Exception e) {
            System.err.println("Dynamic method call failed: " + e.getMessage());
            return null;
        }
    }
    
    private static boolean isAssignable(Class<?> paramType, Class<?> argType) {
        if (paramType.isAssignableFrom(argType)) {
            return true;
        }
        
        // Handle primitive types
        if (paramType == int.class && argType == Integer.class) return true;
        if (paramType == double.class && argType == Double.class) return true;
        if (paramType == boolean.class && argType == Boolean.class) return true;
        // Add more primitive type mappings as needed
        
        return false;
    }
    
    public static void main(String[] args) {
        exploreMethods();
        System.out.println();
        invokeMethodsDynamically();
        
        // Test dynamic method dispatcher
        System.out.println("\n=== Dynamic Method Dispatcher ===");
        Calculator calc = new Calculator();
        
        Object result1 = callMethod(calc, "add", 10, 20);
        System.out.println("Dynamic add(10, 20) = " + result1);
        
        Object result2 = callMethod(calc, "multiply", 3.14, 2.0);
        System.out.println("Dynamic multiply(3.14, 2.0) = " + result2);
    }
}

Working with Constructors

Constructor reflection enables dynamic object creation:

import java.lang.reflect.*;
import java.util.*;

public class ConstructorReflectionExample {
    
    public static class Product {
        private String name;
        private double price;
        private String category;
        
        // Default constructor
        public Product() {
            this("Unknown", 0.0, "General");
        }
        
        // Constructor with name only
        public Product(String name) {
            this(name, 0.0, "General");
        }
        
        // Constructor with name and price
        public Product(String name, double price) {
            this(name, price, "General");
        }
        
        // Full constructor
        public Product(String name, double price, String category) {
            this.name = name;
            this.price = price;
            this.category = category;
        }
        
        @Override
        public String toString() {
            return String.format("Product{name='%s', price=%.2f, category='%s'}", 
                               name, price, category);
        }
    }
    
    public static void exploreConstructors() {
        Class<Product> clazz = Product.class;
        
        System.out.println("=== All Constructors ===");
        Constructor<?>[] constructors = clazz.getDeclaredConstructors();
        
        for (int i = 0; i < constructors.length; i++) {
            Constructor<?> constructor = constructors[i];
            System.out.println("Constructor " + (i + 1) + ":");
            System.out.println("  Modifiers: " + Modifier.toString(constructor.getModifiers()));
            
            // Parameter information
            Parameter[] parameters = constructor.getParameters();
            System.out.print("  Parameters: ");
            if (parameters.length == 0) {
                System.out.println("none");
            } else {
                for (int j = 0; j < parameters.length; j++) {
                    if (j > 0) System.out.print(", ");
                    System.out.print(parameters[j].getType().getSimpleName() + " " + 
                                   parameters[j].getName());
                }
                System.out.println();
            }
            
            // Exception information
            Class<?>[] exceptions = constructor.getExceptionTypes();
            if (exceptions.length > 0) {
                System.out.print("  Throws: ");
                for (int j = 0; j < exceptions.length; j++) {
                    if (j > 0) System.out.print(", ");
                    System.out.print(exceptions[j].getSimpleName());
                }
                System.out.println();
            }
            
            System.out.println();
        }
    }
    
    public static void createObjectsDynamically() {
        Class<Product> clazz = Product.class;
        
        try {
            // Create using default constructor
            Constructor<Product> defaultConstructor = clazz.getDeclaredConstructor();
            Product product1 = defaultConstructor.newInstance();
            System.out.println("Default constructor: " + product1);
            
            // Create using single parameter constructor
            Constructor<Product> nameConstructor = clazz.getDeclaredConstructor(String.class);
            Product product2 = nameConstructor.newInstance("Laptop");
            System.out.println("Name constructor: " + product2);
            
            // Create using two parameter constructor
            Constructor<Product> namePriceConstructor = clazz.getDeclaredConstructor(
                String.class, double.class);
            Product product3 = namePriceConstructor.newInstance("Smartphone", 599.99);
            System.out.println("Name-Price constructor: " + product3);
            
            // Create using full constructor
            Constructor<Product> fullConstructor = clazz.getDeclaredConstructor(
                String.class, double.class, String.class);
            Product product4 = fullConstructor.newInstance("Tablet", 399.99, "Electronics");
            System.out.println("Full constructor: " + product4);
            
        } catch (Exception e) {
            System.err.println("Constructor invocation error: " + e.getMessage());
            e.printStackTrace();
        }
    }
    
    // Generic object factory using reflection
    public static class ReflectionObjectFactory {
        
        public static <T> T createInstance(Class<T> clazz, Object... args) {
            try {
                // Find matching constructor
                Constructor<?>[] constructors = clazz.getDeclaredConstructors();
                
                for (Constructor<?> constructor : constructors) {
                    Class<?>[] paramTypes = constructor.getParameterTypes();
                    
                    if (paramTypes.length == args.length) {
                        boolean matches = true;
                        
                        for (int i = 0; i < args.length; i++) {
                            if (args[i] != null && !isAssignable(paramTypes[i], args[i].getClass())) {
                                matches = false;
                                break;
                            }
                        }
                        
                        if (matches) {
                            constructor.setAccessible(true);
                            return clazz.cast(constructor.newInstance(args));
                        }
                    }
                }
                
                throw new IllegalArgumentException("No matching constructor found");
                
            } catch (Exception e) {
                throw new RuntimeException("Failed to create instance of " + clazz.getName(), e);
            }
        }
        
        private static boolean isAssignable(Class<?> paramType, Class<?> argType) {
            if (paramType.isAssignableFrom(argType)) {
                return true;
            }
            
            // Handle primitive types
            Map<Class<?>, Class<?>> primitiveMap = Map.of(
                int.class, Integer.class,
                double.class, Double.class,
                boolean.class, Boolean.class,
                long.class, Long.class,
                float.class, Float.class,
                char.class, Character.class,
                byte.class, Byte.class,
                short.class, Short.class
            );
            
            return primitiveMap.get(paramType) == argType;
        }
    }
    
    public static void demonstrateObjectFactory() {
        System.out.println("\n=== Object Factory Examples ===");
        
        // Create Product instances using factory
        Product product1 = ReflectionObjectFactory.createInstance(Product.class);
        System.out.println("Factory default: " + product1);
        
        Product product2 = ReflectionObjectFactory.createInstance(Product.class, "Monitor");
        System.out.println("Factory with name: " + product2);
        
        Product product3 = ReflectionObjectFactory.createInstance(Product.class, 
                                                                 "Keyboard", 79.99);
        System.out.println("Factory with name and price: " + product3);
        
        Product product4 = ReflectionObjectFactory.createInstance(Product.class, 
                                                                 "Mouse", 29.99, "Accessories");
        System.out.println("Factory with all parameters: " + product4);
        
        // Create other types
        List<String> list = ReflectionObjectFactory.createInstance(ArrayList.class);
        System.out.println("Factory ArrayList: " + list.getClass().getSimpleName());
        
        String str = ReflectionObjectFactory.createInstance(String.class, "Hello Reflection");
        System.out.println("Factory String: " + str);
    }
    
    public static void main(String[] args) {
        exploreConstructors();
        createObjectsDynamically();
        demonstrateObjectFactory();
    }
}

Real-World Applications

Reflection is extensively used in frameworks and libraries:

import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class ReflectionApplications {
    
    // Simple dependency injection container
    public static class DIContainer {
        private final Map<Class<?>, Object> instances = new ConcurrentHashMap<>();
        private final Map<Class<?>, Class<?>> bindings = new ConcurrentHashMap<>();
        
        public <T> void bind(Class<T> interfaceClass, Class<? extends T> implementationClass) {
            bindings.put(interfaceClass, implementationClass);
        }
        
        @SuppressWarnings("unchecked")
        public <T> T getInstance(Class<T> clazz) {
            // Check if instance already exists (singleton)
            T instance = (T) instances.get(clazz);
            if (instance != null) {
                return instance;
            }
            
            // Check if there's a binding
            Class<?> implementationClass = bindings.getOrDefault(clazz, clazz);
            
            try {
                // Create instance using reflection
                Constructor<?> constructor = implementationClass.getDeclaredConstructors()[0];
                constructor.setAccessible(true);
                
                // Get constructor parameters and resolve dependencies
                Class<?>[] paramTypes = constructor.getParameterTypes();
                Object[] args = new Object[paramTypes.length];
                
                for (int i = 0; i < paramTypes.length; i++) {
                    args[i] = getInstance(paramTypes[i]); // Recursive dependency resolution
                }
                
                instance = clazz.cast(constructor.newInstance(args));
                instances.put(clazz, instance);
                
                return instance;
                
            } catch (Exception e) {
                throw new RuntimeException("Failed to create instance of " + clazz.getName(), e);
            }
        }
    }
    
    // Example classes for DI demonstration
    public interface UserRepository {
        String findUserById(String id);
    }
    
    public static class DatabaseUserRepository implements UserRepository {
        @Override
        public String findUserById(String id) {
            return "User from database: " + id;
        }
    }
    
    public static class UserService {
        private final UserRepository userRepository;
        
        public UserService(UserRepository userRepository) {
            this.userRepository = userRepository;
        }
        
        public String getUser(String id) {
            return userRepository.findUserById(id);
        }
    }
    
    // Simple object mapper (JSON-like)
    public static class SimpleObjectMapper {
        
        public static Map<String, Object> toMap(Object obj) {
            Map<String, Object> map = new HashMap<>();
            Class<?> clazz = obj.getClass();
            
            for (Field field : clazz.getDeclaredFields()) {
                field.setAccessible(true);
                try {
                    Object value = field.get(obj);
                    map.put(field.getName(), value);
                } catch (IllegalAccessException e) {
                    System.err.println("Cannot access field: " + field.getName());
                }
            }
            
            return map;
        }
        
        public static <T> T fromMap(Map<String, Object> map, Class<T> clazz) {
            try {
                T instance = clazz.getDeclaredConstructor().newInstance();
                
                for (Field field : clazz.getDeclaredFields()) {
                    field.setAccessible(true);
                    Object value = map.get(field.getName());
                    
                    if (value != null && isAssignable(field.getType(), value.getClass())) {
                        field.set(instance, value);
                    }
                }
                
                return instance;
                
            } catch (Exception e) {
                throw new RuntimeException("Failed to create object from map", e);
            }
        }
        
        private static boolean isAssignable(Class<?> targetType, Class<?> valueType) {
            return targetType.isAssignableFrom(valueType) ||
                   (targetType.isPrimitive() && isWrapperType(targetType, valueType));
        }
        
        private static boolean isWrapperType(Class<?> primitive, Class<?> wrapper) {
            return (primitive == int.class && wrapper == Integer.class) ||
                   (primitive == double.class && wrapper == Double.class) ||
                   (primitive == boolean.class && wrapper == Boolean.class);
        }
    }
    
    // Test data class
    public static class Employee {
        private String name;
        private int age;
        private double salary;
        
        public Employee() {}
        
        public Employee(String name, int age, double salary) {
            this.name = name;
            this.age = age;
            this.salary = salary;
        }
        
        @Override
        public String toString() {
            return String.format("Employee{name='%s', age=%d, salary=%.2f}", name, age, salary);
        }
    }
    
    public static void demonstrateDependencyInjection() {
        System.out.println("=== Dependency Injection Example ===");
        
        DIContainer container = new DIContainer();
        
        // Bind interface to implementation
        container.bind(UserRepository.class, DatabaseUserRepository.class);
        
        // Get UserService instance (dependencies injected automatically)
        UserService userService = container.getInstance(UserService.class);
        
        String result = userService.getUser("123");
        System.out.println("Result: " + result);
    }
    
    public static void demonstrateObjectMapping() {
        System.out.println("\n=== Object Mapping Example ===");
        
        // Create employee object
        Employee employee = new Employee("John Doe", 30, 75000.0);
        System.out.println("Original: " + employee);
        
        // Convert to map
        Map<String, Object> map = SimpleObjectMapper.toMap(employee);
        System.out.println("As map: " + map);
        
        // Convert back to object
        Employee restored = SimpleObjectMapper.fromMap(map, Employee.class);
        System.out.println("Restored: " + restored);
        
        // Modify map and create new object
        map.put("name", "Jane Smith");
        map.put("age", 28);
        Employee modified = SimpleObjectMapper.fromMap(map, Employee.class);
        System.out.println("Modified: " + modified);
    }
    
    public static void main(String[] args) {
        demonstrateDependencyInjection();
        demonstrateObjectMapping();
    }
}

Performance and Best Practices

import java.lang.reflect.*;
import java.util.concurrent.ConcurrentHashMap;

public class ReflectionBestPractices {
    
    // Cache reflection objects for better performance
    private static final ConcurrentHashMap<String, Method> methodCache = new ConcurrentHashMap<>();
    private static final ConcurrentHashMap<String, Field> fieldCache = new ConcurrentHashMap<>();
    
    public static Method getCachedMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
        String key = clazz.getName() + "." + methodName + "(" + 
                    String.join(",", Arrays.stream(parameterTypes).map(Class::getName).toArray(String[]::new)) + ")";
        
        return methodCache.computeIfAbsent(key, k -> {
            try {
                return clazz.getDeclaredMethod(methodName, parameterTypes);
            } catch (NoSuchMethodException e) {
                throw new RuntimeException("Method not found: " + key, e);
            }
        });
    }
    
    public static Field getCachedField(Class<?> clazz, String fieldName) {
        String key = clazz.getName() + "." + fieldName;
        
        return fieldCache.computeIfAbsent(key, k -> {
            try {
                Field field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);
                return field;
            } catch (NoSuchFieldException e) {
                throw new RuntimeException("Field not found: " + key, e);
            }
        });
    }
    
    // Performance comparison
    public static void performanceComparison() {
        System.out.println("=== Performance Comparison ===");
        
        String testString = "Hello World";
        int iterations = 1_000_000;
        
        // Direct method call
        long startTime = System.nanoTime();
        for (int i = 0; i < iterations; i++) {
            int length = testString.length();
        }
        long directTime = System.nanoTime() - startTime;
        
        // Reflection without caching
        startTime = System.nanoTime();
        try {
            Method lengthMethod = String.class.getMethod("length");
            for (int i = 0; i < iterations; i++) {
                lengthMethod.invoke(testString);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        long reflectionTime = System.nanoTime() - startTime;
        
        // Reflection with caching
        startTime = System.nanoTime();
        try {
            Method cachedMethod = getCachedMethod(String.class, "length");
            for (int i = 0; i < iterations; i++) {
                cachedMethod.invoke(testString);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        long cachedReflectionTime = System.nanoTime() - startTime;
        
        System.out.printf("Direct call: %.2f ms\n", directTime / 1_000_000.0);
        System.out.printf("Reflection (no cache): %.2f ms (%.1fx slower)\n", 
                         reflectionTime / 1_000_000.0, (double) reflectionTime / directTime);
        System.out.printf("Reflection (cached): %.2f ms (%.1fx slower)\n", 
                         cachedReflectionTime / 1_000_000.0, (double) cachedReflectionTime / directTime);
    }
    
    public static void securityConsiderations() {
        System.out.println("\n=== Security Considerations ===");
        
        // Be careful with setAccessible()
        try {
            Field field = System.class.getDeclaredField("security");
            System.out.println("Can access System.security field: " + field != null);
            
            // This might throw SecurityException in restricted environments
            field.setAccessible(true);
            System.out.println("Successfully made field accessible");
            
        } catch (SecurityException e) {
            System.out.println("SecurityException: " + e.getMessage());
        } catch (NoSuchFieldException e) {
            System.out.println("Field not found (expected in newer Java versions)");
        }
        
        // Always check permissions in production code
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            System.out.println("Security manager is active");
        } else {
            System.out.println("No security manager (development environment)");
        }
    }
    
    public static void main(String[] args) {
        performanceComparison();
        securityConsiderations();
    }
}

Summary

Java Reflection is a powerful runtime introspection mechanism:

Key Capabilities:

  • Class Inspection: Examine structure, hierarchy, annotations
  • Dynamic Method Invocation: Call methods by name at runtime
  • Field Access: Read/modify fields including private ones
  • Constructor Access: Create instances dynamically
  • Generic Type Information: Access generic type metadata

Common Use Cases:

  • Frameworks: Spring DI, Hibernate ORM, JAX-RS
  • Serialization: JSON/XML object mapping
  • Testing: Accessing private members for unit tests
  • Configuration: Dynamic configuration and object creation
  • Code Generation: Annotation processors, bytecode manipulation

Best Practices:

  • Cache Reflection Objects: Method, Field, Constructor instances
  • Handle Exceptions: NoSuchMethodException, IllegalAccessException
  • Consider Security: setAccessible() implications
  • Performance Awareness: Reflection is significantly slower
  • Use Sparingly: Prefer compile-time solutions when possible

Performance Tips:

  • Cache Method/Field/Constructor objects
  • Avoid repeated reflection calls in loops
  • Consider alternatives like method handles (Java 7+)
  • Use compile-time code generation when feasible

Security Considerations:

  • setAccessible() bypasses access control
  • May be restricted by SecurityManager
  • Can expose internal implementation details
  • Use with caution in security-sensitive contexts

Reflection enables powerful framework capabilities but should be used judiciously due to performance and security implications. It's essential for understanding how modern Java frameworks operate under the hood.