Java Reflection API

The Reflection API allows you to inspect and manipulate classes, interfaces, fields, methods, and constructors at runtime. It's a powerful feature that enables dynamic code analysis and execution.

1. Basic Reflection Operations

Getting Class Objects

import java.lang.reflect.*;
public class BasicReflection {
public static void main(String[] args) throws Exception {
// Different ways to get Class object
Class<String> stringClass1 = String.class;
Class<?> stringClass2 = Class.forName("java.lang.String");
Class<?> stringClass3 = "hello".getClass();
System.out.println("Class name: " + stringClass1.getName());
System.out.println("Simple name: " + stringClass1.getSimpleName());
System.out.println("Canonical name: " + stringClass1.getCanonicalName());
System.out.println("Is interface: " + stringClass1.isInterface());
System.out.println("Modifiers: " + Modifier.toString(stringClass1.getModifiers()));
}
}

Inspecting Class Structure

import java.lang.reflect.*;
import java.util.*;
class Person {
private String name;
private int age;
public static int count = 0;
public Person() {}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
private void privateMethod() {
System.out.println("Private method called");
}
}
public class ClassInspection {
public static void main(String[] args) throws Exception {
Class<Person> personClass = Person.class;
// Inspect fields
System.out.println("=== FIELDS ===");
Field[] fields = personClass.getDeclaredFields();
for (Field field : fields) {
System.out.println("Field: " + field.getName() + 
" Type: " + field.getType() +
" Modifiers: " + Modifier.toString(field.getModifiers()));
}
// Inspect methods
System.out.println("\n=== METHODS ===");
Method[] methods = personClass.getDeclaredMethods();
for (Method method : methods) {
System.out.println("Method: " + method.getName() +
" Return: " + method.getReturnType() +
" Params: " + Arrays.toString(method.getParameterTypes()));
}
// Inspect constructors
System.out.println("\n=== CONSTRUCTORS ===");
Constructor<?>[] constructors = personClass.getDeclaredConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println("Constructor: " + constructor.getName() +
" Params: " + Arrays.toString(constructor.getParameterTypes()));
}
}
}

2. Working with Fields

Accessing and Modifying Fields

import java.lang.reflect.*;
public class FieldManipulation {
public static void main(String[] args) throws Exception {
Person person = new Person("John", 25);
// Access private field
Field nameField = Person.class.getDeclaredField("name");
nameField.setAccessible(true); // Bypass access control
// Get field value
String nameValue = (String) nameField.get(person);
System.out.println("Current name: " + nameValue);
// Set field value
nameField.set(person, "Jane");
System.out.println("New name: " + person.getName());
// Access static field
Field countField = Person.class.getDeclaredField("count");
countField.setAccessible(true);
System.out.println("Static count: " + countField.get(null));
// Modify static field
countField.set(null, 42);
System.out.println("Modified static count: " + countField.get(null));
}
}

Field Type Information

import java.lang.reflect.*;
import java.util.*;
public class FieldAnalysis {
private String name;
private List<Integer> numbers;
private Map<String, Object> data;
private int[] array;
public static void main(String[] args) throws Exception {
Class<FieldAnalysis> clazz = FieldAnalysis.class;
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
System.out.println("\nField: " + field.getName());
System.out.println("Type: " + field.getType());
System.out.println("Generic Type: " + field.getGenericType());
// Check if it's a parameterized type
if (field.getGenericType() instanceof ParameterizedType) {
ParameterizedType pType = (ParameterizedType) field.getGenericType();
System.out.println("Raw type: " + pType.getRawType());
System.out.println("Type arguments: " + Arrays.toString(pType.getActualTypeArguments()));
}
// Check if it's an array
if (field.getType().isArray()) {
System.out.println("Array component type: " + field.getType().getComponentType());
}
}
}
}

3. Working with Methods

Invoking Methods

import java.lang.reflect.*;
public class MethodInvocation {
public static void main(String[] args) throws Exception {
Person person = new Person("Alice", 30);
// Get public method and invoke
Method getNameMethod = Person.class.getMethod("getName");
String name = (String) getNameMethod.invoke(person);
System.out.println("Name via reflection: " + name);
// Get method with parameters and invoke
Method setAgeMethod = Person.class.getMethod("setAge", int.class);
setAgeMethod.invoke(person, 35);
System.out.println("New age: " + person.getAge());
// Invoke private method
Method privateMethod = Person.class.getDeclaredMethod("privateMethod");
privateMethod.setAccessible(true);
privateMethod.invoke(person);
// Invoke static method (if existed)
// Method staticMethod = Person.class.getMethod("staticMethod");
// staticMethod.invoke(null); // null for static methods
}
}

Method Parameter Inspection

import java.lang.reflect.*;
import java.util.*;
class Calculator {
public int add(int a, int b) { return a + b; }
public double multiply(double x, double y) { return x * y; }
private void process(String text, int count, boolean flag) {}
}
public class MethodAnalysis {
public static void main(String[] args) throws Exception {
Class<Calculator> clazz = Calculator.class;
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
System.out.println("\nMethod: " + method.getName());
System.out.println("Return type: " + method.getReturnType());
System.out.println("Generic return type: " + method.getGenericReturnType());
// Parameter information
Parameter[] parameters = method.getParameters();
Type[] genericParamTypes = method.getGenericParameterTypes();
System.out.println("Parameters:");
for (int i = 0; i < parameters.length; i++) {
System.out.println("  " + parameters[i].getName() + 
" : " + parameters[i].getType() +
" (generic: " + genericParamTypes[i] + ")");
}
// Exception information
Type[] exceptionTypes = method.getGenericExceptionTypes();
if (exceptionTypes.length > 0) {
System.out.println("Exceptions: " + Arrays.toString(exceptionTypes));
}
}
}
}

4. Working with Constructors

Creating Instances

import java.lang.reflect.*;
public class ConstructorExample {
static class Product {
private String name;
private double price;
public Product() {
this.name = "Unknown";
this.price = 0.0;
}
public Product(String name) {
this.name = name;
this.price = 0.0;
}
private Product(String name, double price) {
this.name = name;
this.price = price;
}
@Override
public String toString() {
return "Product{name='" + name + "', price=" + price + "}";
}
}
public static void main(String[] args) throws Exception {
// Get default constructor and create instance
Constructor<Product> defaultConstructor = Product.class.getConstructor();
Product product1 = defaultConstructor.newInstance();
System.out.println("Default: " + product1);
// Get constructor with parameters
Constructor<Product> nameConstructor = Product.class.getConstructor(String.class);
Product product2 = nameConstructor.newInstance("Laptop");
System.out.println("Name only: " + product2);
// Get private constructor
Constructor<Product> privateConstructor = Product.class.getDeclaredConstructor(String.class, double.class);
privateConstructor.setAccessible(true);
Product product3 = privateConstructor.newInstance("Phone", 699.99);
System.out.println("Private constructor: " + product3);
// Get all constructors
System.out.println("\nAll constructors:");
Constructor<?>[] constructors = Product.class.getDeclaredConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor + " - accessible: " + constructor.isAccessible());
}
}
}

5. Advanced Reflection Features

Dynamic Proxy

import java.lang.reflect.*;
import java.util.*;
interface Service {
void serve();
String process(String input);
}
class RealService implements Service {
public void serve() {
System.out.println("RealService: serving");
}
public String process(String input) {
return "Processed: " + input;
}
}
public class DynamicProxyExample {
public static void main(String[] args) {
RealService realService = new RealService();
// Create dynamic proxy
Service proxy = (Service) Proxy.newProxyInstance(
Service.class.getClassLoader(),
new Class<?>[] { Service.class },
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method: " + method.getName());
if (args != null) {
System.out.println("Arguments: " + Arrays.toString(args));
}
// Call real method
Object result = method.invoke(realService, args);
System.out.println("After method: " + method.getName());
System.out.println("Result: " + result);
return result;
}
}
);
// Use proxy
proxy.serve();
proxy.process("test input");
}
}

Annotation Processing

import java.lang.annotation.*;
import java.lang.reflect.*;
// Custom annotations
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@interface MyAnnotation {
String value() default "";
int version() default 1;
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface Inject {
String dependency();
}
// Annotated class
@MyAnnotation(value = "ClassAnnotation", version = 2)
class AnnotatedClass {
@MyAnnotation("FieldAnnotation")
@Inject(dependency = "userService")
private String data;
@MyAnnotation("MethodAnnotation")
public void annotatedMethod() {}
}
public class AnnotationProcessing {
public static void main(String[] args) throws Exception {
Class<AnnotatedClass> clazz = AnnotatedClass.class;
// Class annotations
MyAnnotation classAnnotation = clazz.getAnnotation(MyAnnotation.class);
if (classAnnotation != null) {
System.out.println("Class annotation: " + classAnnotation.value() + 
" version: " + classAnnotation.version());
}
// Field annotations
Field field = clazz.getDeclaredField("data");
MyAnnotation fieldAnnotation = field.getAnnotation(MyAnnotation.class);
Inject injectAnnotation = field.getAnnotation(Inject.class);
if (fieldAnnotation != null) {
System.out.println("Field annotation: " + fieldAnnotation.value());
}
if (injectAnnotation != null) {
System.out.println("Inject dependency: " + injectAnnotation.dependency());
}
// Method annotations
Method method = clazz.getMethod("annotatedMethod");
MyAnnotation methodAnnotation = method.getAnnotation(MyAnnotation.class);
if (methodAnnotation != null) {
System.out.println("Method annotation: " + methodAnnotation.value());
}
// Get all annotations
System.out.println("\nAll class annotations:");
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation.annotationType().getName());
}
}
}

6. Real-World Examples

Dependency Injection Framework

import java.lang.reflect.*;
import java.util.*;
class SimpleDIContainer {
private Map<Class<?>, Object> instances = new HashMap<>();
private Map<Class<?>, Class<?>> implementations = new HashMap<>();
public <T> void register(Class<T> interfaceType, Class<? extends T> implementation) {
implementations.put(interfaceType, implementation);
}
@SuppressWarnings("unchecked")
public <T> T resolve(Class<T> type) throws Exception {
if (instances.containsKey(type)) {
return (T) instances.get(type);
}
Class<?> implementation = implementations.getOrDefault(type, type);
Constructor<?> constructor = implementation.getDeclaredConstructors()[0];
// Resolve constructor parameters
Class<?>[] paramTypes = constructor.getParameterTypes();
Object[] params = new Object[paramTypes.length];
for (int i = 0; i < paramTypes.length; i++) {
params[i] = resolve(paramTypes[i]);
}
T instance = (T) constructor.newInstance(params);
instances.put(type, instance);
return instance;
}
}
// Example usage
interface UserService {
void createUser(String name);
}
interface EmailService {
void sendEmail(String to, String message);
}
class UserServiceImpl implements UserService {
private EmailService emailService;
public UserServiceImpl(EmailService emailService) {
this.emailService = emailService;
}
public void createUser(String name) {
System.out.println("Creating user: " + name);
emailService.sendEmail(name + "@example.com", "Welcome!");
}
}
class EmailServiceImpl implements EmailService {
public void sendEmail(String to, String message) {
System.out.println("Sending email to " + to + ": " + message);
}
}
public class DIExample {
public static void main(String[] args) throws Exception {
SimpleDIContainer container = new SimpleDIContainer();
container.register(UserService.class, UserServiceImpl.class);
container.register(EmailService.class, EmailServiceImpl.class);
UserService userService = container.resolve(UserService.class);
userService.createUser("John Doe");
}
}

Object-to-JSON Serializer

import java.lang.reflect.*;
import java.util.*;
public class JsonSerializer {
public static String toJson(Object obj) throws Exception {
if (obj == null) return "null";
Class<?> clazz = obj.getClass();
if (clazz.isPrimitive() || isWrapperType(clazz)) {
return String.valueOf(obj);
} else if (clazz.equals(String.class)) {
return "\"" + escapeString(obj.toString()) + "\"";
} else if (clazz.isArray()) {
return arrayToJson(obj);
} else if (Collection.class.isAssignableFrom(clazz)) {
return collectionToJson((Collection<?>) obj);
} else if (Map.class.isAssignableFrom(clazz)) {
return mapToJson((Map<?, ?>) obj);
} else {
return objectToJson(obj);
}
}
private static String objectToJson(Object obj) throws Exception {
Class<?> clazz = obj.getClass();
StringBuilder json = new StringBuilder("{");
Field[] fields = clazz.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
field.setAccessible(true);
String fieldName = field.getName();
Object value = field.get(obj);
json.append("\"").append(fieldName).append("\": ").append(toJson(value));
if (i < fields.length - 1) {
json.append(", ");
}
}
json.append("}");
return json.toString();
}
private static String arrayToJson(Object array) throws Exception {
int length = Array.getLength(array);
StringBuilder json = new StringBuilder("[");
for (int i = 0; i < length; i++) {
Object element = Array.get(array, i);
json.append(toJson(element));
if (i < length - 1) {
json.append(", ");
}
}
json.append("]");
return json.toString();
}
private static String collectionToJson(Collection<?> collection) throws Exception {
StringBuilder json = new StringBuilder("[");
Iterator<?> iterator = collection.iterator();
while (iterator.hasNext()) {
Object element = iterator.next();
json.append(toJson(element));
if (iterator.hasNext()) {
json.append(", ");
}
}
json.append("]");
return json.toString();
}
private static String mapToJson(Map<?, ?> map) throws Exception {
StringBuilder json = new StringBuilder("{");
Iterator<?> iterator = map.keySet().iterator();
while (iterator.hasNext()) {
Object key = iterator.next();
Object value = map.get(key);
json.append("\"").append(key).append("\": ").append(toJson(value));
if (iterator.hasNext()) {
json.append(", ");
}
}
json.append("}");
return json.toString();
}
private static boolean isWrapperType(Class<?> clazz) {
return clazz.equals(Integer.class) || clazz.equals(Long.class) ||
clazz.equals(Double.class) || clazz.equals(Float.class) ||
clazz.equals(Boolean.class) || clazz.equals(Short.class) ||
clazz.equals(Byte.class) || clazz.equals(Character.class);
}
private static String escapeString(String str) {
return str.replace("\"", "\\\"")
.replace("\\", "\\\\")
.replace("/", "\\/")
.replace("\b", "\\b")
.replace("\f", "\\f")
.replace("\n", "\\n")
.replace("\r", "\\r")
.replace("\t", "\\t");
}
}
// Test the serializer
class TestObject {
private String name = "John";
private int age = 30;
private boolean active = true;
private String[] tags = {"java", "reflection"};
private List<Integer> scores = Arrays.asList(95, 87, 92);
// getters and setters...
}
public class SerializerExample {
public static void main(String[] args) throws Exception {
TestObject obj = new TestObject();
String json = JsonSerializer.toJson(obj);
System.out.println(json);
}
}

7. Performance Considerations and Best Practices

import java.lang.reflect.*;
import java.util.concurrent.*;
public class ReflectionPerformance {
private static final int ITERATIONS = 1000000;
// Regular method call
public static int directCall(Person person) {
return person.getAge();
}
// Reflection method call
public static int reflectionCall(Person person) throws Exception {
Method method = Person.class.getMethod("getAge");
return (int) method.invoke(person);
}
// Cached reflection call
private static final Method CACHED_METHOD;
static {
try {
CACHED_METHOD = Person.class.getMethod("getAge");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static int cachedReflectionCall(Person person) throws Exception {
return (int) CACHED_METHOD.invoke(person);
}
public static void main(String[] args) throws Exception {
Person person = new Person("Test", 25);
// Warm up
for (int i = 0; i < 1000; i++) {
directCall(person);
reflectionCall(person);
cachedReflectionCall(person);
}
// Benchmark direct calls
long start = System.nanoTime();
for (int i = 0; i < ITERATIONS; i++) {
directCall(person);
}
long directTime = System.nanoTime() - start;
// Benchmark reflection calls
start = System.nanoTime();
for (int i = 0; i < ITERATIONS; i++) {
reflectionCall(person);
}
long reflectionTime = System.nanoTime() - start;
// Benchmark cached reflection calls
start = System.nanoTime();
for (int i = 0; i < ITERATIONS; i++) {
cachedReflectionCall(person);
}
long cachedReflectionTime = System.nanoTime() - start;
System.out.println("Direct calls: " + directTime / 1000000 + " ms");
System.out.println("Reflection calls: " + reflectionTime / 1000000 + " ms");
System.out.println("Cached reflection calls: " + cachedReflectionTime / 1000000 + " ms");
}
}

Key Points and Best Practices

  1. Performance: Reflection is slower than direct calls - cache Method/Field/Constructor objects when possible
  2. Security: Use setAccessible(true) carefully as it bypasses access controls
  3. Exception Handling: Reflection methods throw checked exceptions that must be handled
  4. Type Safety: Loss of compile-time type safety - use generics and casting carefully
  5. Use Cases: Frameworks, ORM tools, dependency injection, testing tools
  6. Alternatives: Consider Method Handles (Java 7+) for better performance

Reflection is powerful but should be used judiciously due to performance overhead and reduced type safety.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper