Java Reflection

Learn about Java Reflection API. Understand how to inspect classes, interfaces, fields, and methods at runtime.

Reflection is an API that allows a program to inspect and manipulate the internal properties of classes, interfaces, fields, and methods at runtime.

It allows you to:

  • Inspect classes, interfaces, fields, and methods at runtime.
  • Instantiate new objects, invoke methods, and get/set field values.
  • Access private members of a class.

The java.lang.reflect package contains the classes required for reflection.


1. Getting Class Object

The entry point for all reflection operations is the java.lang.Class object.

// 1. Using .class syntax
Class<?> c1 = String.class;

// 2. Using getClass() method
String s = "Hello";
Class<?> c2 = s.getClass();

// 3. Using Class.forName()
try {
    Class<?> c3 = Class.forName("java.lang.String");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

2. Inspecting Methods

You can get information about methods declared in a class.

import java.lang.reflect.Method;

public class Main {
    public static void main(String[] args) {
        Class<String> stringClass = String.class;
        Method[] methods = stringClass.getDeclaredMethods();

        for (Method method : methods) {
            System.out.println("Method Name: " + method.getName());
            System.out.println("Return Type: " + method.getReturnType());
            System.out.println("Parameter Count: " + method.getParameterCount());
            System.out.println("---");
        }
    }
}

3. Inspecting Fields

You can access fields, even private ones.

import java.lang.reflect.Field;

class Person {
    private String name = "John";
}

public class Main {
    public static void main(String[] args) throws Exception {
        Person p = new Person();
        Class<?> cls = p.getClass();

        Field field = cls.getDeclaredField("name");
        field.setAccessible(true); // Allow access to private field

        System.out.println("Original Value: " + field.get(p));

        field.set(p, "Doe"); // Change value
        System.out.println("New Value: " + field.get(p));
    }
}

4. Invoking Methods

You can invoke methods dynamically.

import java.lang.reflect.Method;

class Calculator {
    public void add(int a, int b) {
        System.out.println("Sum: " + (a + b));
    }
}

public class Main {
    public static void main(String[] args) throws Exception {
        Calculator calc = new Calculator();
        Class<?> cls = calc.getClass();

        Method method = cls.getMethod("add", int.class, int.class);
        method.invoke(calc, 10, 20); // Output: Sum: 30
    }
}

Common Pitfalls

[!WARNING] Performance Overhead: Reflection involves dynamic type resolving, which is slower than direct code execution. Avoid using it in performance-critical sections.

[!CAUTION] Security Restrictions: Reflection can break encapsulation (accessing private members). This can lead to security vulnerabilities if not used carefully.

[!NOTE] Compile-time Safety: You lose compile-time type safety. Errors that would normally be caught at compile time might occur at runtime.


Key Takeaways

  • Runtime Inspection: Reflection allows code to inspect itself.
  • Powerful: Can access private members and invoke methods dynamically.
  • Use Cases: Frameworks (Spring, Hibernate), JUnit, Dependency Injection containers.
  • Trade-offs: Slower performance and potential security risks.