What is Reflection in Java?
Reflection is a powerful feature in Java. It provides the ability for a Java program to inspect and manipulate its own structure during execution. You can find information about classes, such as their names, methods, and fields, without knowing them at compile time.
Reflection offers several key benefits:
- Extensibility Features: Frameworks like Spring and Hibernate use reflection. They read annotations and class structures to provide features like dependency injection or object-relational mapping (ORM).
- Debugging Tools: Debuggers use reflection to inspect object states and method calls at runtime.
- Testing Frameworks: JUnit uses reflection to find and execute test methods within a class.
- Dynamic Code Loading: You can load classes dynamically based on user input or configuration. This allows for more flexible applications.
- Serialization: Libraries that serialize objects to JSON or XML often use reflection to access object fields.
Is Reflection an API?
Reflection is not an external API like a web service or a third-party library. Instead, Reflection is a core feature of the Java language and its standard library. The java.lang.reflect
package provides the classes and interfaces that enable this feature. These classes collectively form the Application Programming Interface (API) for using reflection within your Java programs. So, while you use an API to work with reflection, reflection itself is a built-in capability of Java.
Core Reflection Classes
The java.lang.reflect
package provides the classes for reflection. Here are the main ones:
Class
: This is the entry point for reflection. It represents classes and interfaces in a running Java application.Constructor
: This class represents a single constructor of a class.Method
: This class represents a single method of a class or interface.Field
: This class represents a single field (a member variable) of a class or interface.
How to Use Reflection
You start reflection by getting a Class
object. There are three common ways to do this.
1. Using Class.forName()
This method is useful when you have the class name as a string.
Java
public class ReflectionClassForName {
public static void main(String[] args) {
try {
Class<?> myClass = Class.forName("java.lang.String");
System.out.println("Class Name: " + myClass.getName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
This code loads the String
class. It then prints its full name.
2. Using object.getClass()
If you already have an object instance, you can get its Class
object.
Java
public class ReflectionGetObjectClass {
public static void main(String[] args) {
String myString = "Hello Reflection";
Class<?> myClass = myString.getClass();
System.out.println("Class Name: " + myClass.getName());
}
}
This example gets the Class
object for a String
instance.
3. Using .class Literal
This is the simplest way when you know the class at compile time.
Java
public class ReflectionClassLiteral {
public static void main(String[] args) {
Class<?> myClass = Integer.class;
System.out.println("Class Name: " + myClass.getName());
}
}
This code directly gets the Class
object for Integer
.
Examples of Reflection in Action
Let us explore practical examples using reflection.
Example 1: Inspecting Class Fields
You can get information about a class fields, including private ones.
Java
import java.lang.reflect.Field;
class Person {
private String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
public class ReflectFields {
public static void main(String[] args) {
try {
Class<?> personClass = Class.forName("Person");
System.out.println("Public Fields:");
Field[] publicFields = personClass.getFields(); // Gets public fields
for (Field field : publicFields) {
System.out.println(" " + field.getName());
}
System.out.println("\nDeclared Fields (including private):");
Field[] allFields = personClass.getDeclaredFields(); // Gets all declared fields
for (Field field : allFields) {
System.out.println(" " + field.getName());
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
This code retrieves and prints both public and private fields of the Person
class.
Example 2: Inspecting Class Methods
You can list all methods in a class.
Java
import java.lang.reflect.Method;
class Calculator {
public int add(int a, int b) {
return a + b;
}
private void secretMethod() {
System.out.println("This is a secret method.");
}
}
public class ReflectMethods {
public static void main(String[] args) {
try {
Class<?> calculatorClass = Class.forName("Calculator");
System.out.println("Public Methods:");
Method[] publicMethods = calculatorClass.getMethods(); // Gets public methods, including inherited
for (Method method : publicMethods) {
System.out.println(" " + method.getName());
}
System.out.println("\nDeclared Methods (including private):");
Method[] declaredMethods = calculatorClass.getDeclaredMethods(); // Gets all declared methods
for (Method method : declaredMethods) {
System.out.println(" " + method.getName());
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
This example shows how to get both public and all declared methods of a class.
Example 3: Invoking Methods Dynamically
Reflection allows you to call methods at runtime.
Java
import java.lang.reflect.Method;
class Greeter {
public void sayHello(String name) {
System.out.println("Hello, " + name + "!");
}
}
public class InvokeMethod {
public static void main(String[] args) {
try {
Class<?> greeterClass = Class.forName("Greeter");
Object greeterInstance = greeterClass.getDeclaredConstructor().newInstance(); // Create an instance
Method sayHelloMethod = greeterClass.getMethod("sayHello", String.class);
sayHelloMethod.invoke(greeterInstance, "World"); // Invoke the method
} catch (Exception e) {
e.printStackTrace();
}
}
}
This code gets the sayHello
method and then invokes it on a Greeter
object.
Example 4: Accessing and Modifying Private Fields
You can even access and change private fields using reflection. Be careful with this, as it can break encapsulation.
Java
import java.lang.reflect.Field;
class Account {
private double balance;
public Account(double balance) {
this.balance = balance;
}
public double getBalance() {
return balance;
}
}
public class AccessPrivateField {
public static void main(String[] args) {
try {
Account myAccount = new Account(100.0);
System.out.println("Initial Balance: " + myAccount.getBalance());
Field balanceField = Account.class.getDeclaredField("balance");
balanceField.setAccessible(true); // Allow access to private field
balanceField.set(myAccount, 200.0); // Set new value
System.out.println("New Balance: " + myAccount.getBalance());
} catch (Exception e) {
e.printStackTrace();
}
}
}
This example shows how to get a private field, make it accessible, and change its value.
When to Use and When to Avoid Reflection
Use Reflection when:
- You build frameworks that need to interact with user-defined classes.
- You need to inspect or modify code at runtime for debugging or testing.
- You create tools that analyze or generate code.
Avoid Reflection when:
- Performance is critical. Reflection operations are slower than direct calls.
- You can achieve the same result with normal object-oriented programming.
- It breaks encapsulation. Direct access to private members can make code harder to maintain and debug.