Java Reflection
Definition of Java Reflection
Reflection is a programming technique for analyzing class information through an object. The word also has dictionary meanings such as projection and reflection.
Java reflection can find classes, interfaces, and methods, create objects, change variables, and call methods. Reflection is part of the Java standard API.
Reflection can retrieve the following information. With that information, you can create objects, call methods, or change variable values.
| Class | Package |
|---|---|
| Class | java.lang |
| Constructor | java.lang.reflect |
| Method | java.lang.reflect |
| Field | java.lang.reflect |
What It Means to Understand Reflection
Frameworks such as Spring use a lot of reflection internally. Understanding reflection therefore has the following benefits.
- You can read and understand framework source code.
- You can imagine how a framework works internally even without reading its source code.
- You can build your own framework.
- In modern Java, existing OSS (Open Source Software) frameworks are often used, so building one yourself is probably uncommon.
Learn reflection and step up from beginner to intermediate Java engineer.
Normally, you should be careful because using reflection in business logic and similar code is not recommended. The reason is explained later.
Reflection Basics
Getting the Class Class
To get a class, use the Class class. The following are ways to use the Class class.
Method 1
Class<ClassName> objectName = ClassName.class;
Method 2
ClassName objectName1 = new ClassName();
Class<? extends ClassName> objectName2 = objectName1.getClass();
Method 3
Class<?> objectName1 = Class.forName("ClassName");
When using the forName method, you must handle the ClassNotFoundException exception.
You can also handle it with ReflectiveOperationException, the common exception class for reflection-related exceptions.
Creating an Instance
The following is how to create an instance.
// Before Java 9
Class<ClassName> objectName1 = ClassName.class;
Object objectName2 = objectName1.newInstance(); // @Deprecated(since="9")
// Java 9 and later
Class<ClassName> objectName1 = ClassName.class;
Object objectName2 = objectName1.getDeclaredConstructor().newInstance()
This corresponds to the following.
ClassName objectName2 = new ClassName();
Getting and Running Methods
To get a method, store the return value of the getMethod method in a Method class object.
Method objectName = "Class class object name".getMethod("methodName", "argument1 type name.class", "argument2 type name.class", ...);
The second and later arguments of the getMethod method are variable arguments of the Class class.
To run the method, execute the invoke method.
"Method class object".invoke("created instance", "argument1", "argument2", ...)
The second and later arguments of the invoke method are variable arguments.
Example of Creating an Instance, Getting Methods, and Running Them
Let’s look at the content above with an example program.
The code below has simple set and get methods.
package com.devkuma.basic.reflection.ex1;
public class Foo {
private String str;
public void setStr(String str) {
this.str = str;
}
public String getStr() {
return str;
}
}
The following code creates a Foo object and runs the setStr and getStr methods.
package com.devkuma.basic.reflection.ex1;
import java.lang.reflect.Method;
public class Main {
public static void main(String[] args) {
try {
// Get the class
Class<?> fooClazz = Class.forName("com.devkuma.basic.reflection.ex1.Foo");
// Create an instance
Object myObj = fooClazz.getDeclaredConstructor().newInstance();
// Get the method (setStr)
Method setStrMethod = fooClazz.getMethod("setStr", String.class);
// Run the method (setStr)
setStrMethod.invoke(myObj, "test");
// Get the method (getStr)
Method getStrMethod = fooClazz.getMethod("getStr");
// Run the method (getStr)
System.out.println(getStrMethod.invoke(myObj));
} catch (ReflectiveOperationException e) {
e.printStackTrace();
}
}
}
Execution result:
test
In the example above, strings such as the class name and method name are passed as arguments to methods such as forName, getMethod, and invoke.
Referencing and Changing private Fields
This section looks at how to reference and change private fields.
Check it with the following example code.
package com.devkuma.basic.reflection.ex1;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Main {
public static void main(String[] args) {
try {
// Get the class
Class<?> fooClazz = Class.forName("com.devkuma.basic.reflection.ex1.Foo");
// Create an instance
Object myObj = fooClazz.getDeclaredConstructor().newInstance();
// Get the method (setStr)
Method setStrMethod = fooClazz.getMethod("setStr", String.class);
// Run the method (setStr)
setStrMethod.invoke(myObj, "test");
// Get the method (getStr)
Method getStrMethod = fooClazz.getMethod("getStr");
// Run the method (getStr)
System.out.println(getStrMethod.invoke(myObj));
// Get the field (str)
Field strField = fooClazz.getDeclaredField("str");
strField.setAccessible(true);
System.out.println(strField.get(myObj));
// Change the field (str)
strField.set(myObj, "test2");
System.out.println(strField.get(myObj));
} catch (ReflectiveOperationException e) {
e.printStackTrace();
}
}
}
Execution result:
test
test2
In this example code, the value is first stored in the field with the setStr method.
That value is retrieved by getting the field as the strField object of the Field class and using the get method. Then the value is changed from the strField object with the set method.
Risks of Reflection
When using reflection, keep the following points in mind.
Risk of Breaking Class Design
As described so far, values encapsulated with the private access modifier can be retrieved and changed directly.
Because reflection makes this kind of direct manipulation possible, it carries the risk of breaking the original class design.
Compile-Time Errors Are Not Detected
Previously, code that used reflection was outside the scope of compile-time error detection, so errors could not be detected during compilation.
Errors in reflection code could only be detected when the program ran.
However, since Java 7, ReflectiveOperationException has defined a common exception class for reflection-related exceptions, so they can be handled.
Summary
This page explained reflection.
With reflection, you can run another class or method later simply by changing values assigned to variables such as class names or method names. It is useful when testing programs and in similar situations.