Session 19|Java Reflection¶
What is Reflection?¶
Reflection in Java is used to examine classes, methods, fields, and interfaces at runtime. It also allows you to change the behavior of a class dynamically.
For example, you can use reflection to:
- Inspect all methods present in a class.
- Inspect all fields present in a class.
- Determine the return type of a method.
- Retrieve the modifiers of a class.
- Check which interfaces a class implements.
- Modify the value of both public and private fields of a class.
How to Perform Reflection on Classes?¶
To reflect on a class you first need to obtain an object of type Class.
What is the Class class?
- The
Classclass is an instance that represents a class during runtime. - The JVM creates one
Classobject for each class loaded at runtime. - This
Classobject contains metadata information about the class, such as its methods, fields, constructors, etc.
How to Obtain a Class Object?¶
There are 3 ways to obtain the Class object for a particular class:
1. Using the forName(String className) Method¶
In [1]:
public class Bird {}
// it works in .java file
try {
Class birdClass = Class.forName("Bird");
System.out.println(birdClass); // class Bird
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
java.lang.ClassNotFoundException: Bird at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:445) at jdk.jshell/jdk.jshell.execution.DefaultLoaderDelegate$RemoteClassLoader.findClass(DefaultLoaderDelegate.java:154) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:593) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) at java.base/java.lang.Class.forName0(Native Method) at java.base/java.lang.Class.forName(Class.java:421) at java.base/java.lang.Class.forName(Class.java:412) at REPL.$JShell$13.do_it$($JShell$13.java:19) at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) at java.base/java.lang.reflect.Method.invoke(Method.java:580) at io.github.spencerpark.ijava.execution.IJavaExecutionControl.lambda$execute$1(IJavaExecutionControl.java:95) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) at java.base/java.lang.Thread.run(Thread.java:1583)
2. Using .class¶
In [2]:
Class birdClass = Bird.class;
birdClass; // class Bird
Out[2]:
class REPL.$JShell$12$Bird
3. Using getClass() method¶
In [3]:
Bird bird = new Bird();
Class birdClass = bird.getClass();
birdClass;
Out[3]:
class REPL.$JShell$12$Bird
Reflection on Classes¶
In [4]:
import java.lang.reflect.Modifier;
import java.util.Arrays;
public class Eagle {
public String breed;
private boolean canSwim;
public void fly() {
System.out.println("Fly");
}
public void eat() {
System.out.println("Eat");
}
}
In [5]:
Class eagle = Eagle.class;
eagle.getClass();
Out[5]:
class java.lang.Class
In [6]:
eagle.getName(); // Eagle
Out[6]:
REPL.$JShell$20$Eagle
In [7]:
Modifier.toString(eagle.getModifiers());
Out[7]:
public static
In [8]:
Arrays.toString(eagle.getMethods());
Out[8]:
[public void REPL.$JShell$20$Eagle.fly(), public void REPL.$JShell$20$Eagle.eat(), public boolean java.lang.Object.equals(java.lang.Object), public java.lang.String java.lang.Object.toString(), public native int java.lang.Object.hashCode(), public final native java.lang.Class java.lang.Object.getClass(), public final native void java.lang.Object.notify(), public final native void java.lang.Object.notifyAll(), public final void java.lang.Object.wait(long) throws java.lang.InterruptedException, public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException, public final void java.lang.Object.wait() throws java.lang.InterruptedException]
Reflection on Methods¶
In [9]:
import java.lang.reflect.Method;
Class eagleClass = Eagle.class;
Method[] methods = eagleClass.getMethods(); // return all public methods including Object class methods
int x = 0;
for (Method method: methods) {
System.out.println("Method name - " + method.getName());
System.out.println("Method name - " + method.getReturnType());
System.out.println("Class name - " + method.getDeclaringClass());
x += 1;
if (x < methods.length) {
System.out.println("------------");
}
}
Method name - fly Method name - void Class name - class REPL.$JShell$20$Eagle ------------ Method name - eat Method name - void Class name - class REPL.$JShell$20$Eagle ------------ Method name - equals Method name - boolean Class name - class java.lang.Object ------------ Method name - toString Method name - class java.lang.String Class name - class java.lang.Object ------------ Method name - hashCode Method name - int Class name - class java.lang.Object ------------ Method name - getClass Method name - class java.lang.Class Class name - class java.lang.Object ------------ Method name - notify Method name - void Class name - class java.lang.Object ------------ Method name - notifyAll Method name - void Class name - class java.lang.Object ------------ Method name - wait Method name - void Class name - class java.lang.Object ------------ Method name - wait Method name - void Class name - class java.lang.Object ------------ Method name - wait Method name - void Class name - class java.lang.Object
In [10]:
Class eagle = Eagle.class;
Method[] methods = eagle.getDeclaredMethods(); // return all public & private methods
int y = 0;
for (Method method: methods) {
System.out.println("Method name - " + method.getName());
System.out.println("Method name - " + method.getReturnType());
System.out.println("Class name - " + method.getDeclaringClass());
y += 1;
if (y < methods.length) {
System.out.println("------------");
}
}
Method name - fly Method name - void Class name - class REPL.$JShell$20$Eagle ------------ Method name - eat Method name - void Class name - class REPL.$JShell$20$Eagle
Invoking Methods Using Reflection¶
In [11]:
public class Eagle2 {
Eagle2() {}
public void fly(int intPara, boolean boolPara, String strPara) {
System.out.println("Fly " + intPara + " " + boolPara + " " + strPara);;
}
}
In [12]:
Class eagle = Eagle2.class;
Object eagleObj = eagle.newInstance(); // invoke empty eagle constructor
Method flyMethod = eagle.getMethod("fly", int.class, boolean.class, String.class); // fly method
flyMethod.invoke(eagleObj, 1, true, "hello");
Fly 1 true hello
Reflection on Fields¶
In [13]:
import java.lang.reflect.Field;
Class eagle = Eagle.class;
Field[] fields = eagle.getFields(); // return only public fields
for (Field field: fields) {
System.out.println("Field Name - " + field.getName());
System.out.println("Type Name - " + field.getType());
System.out.println("Modifier Name - " + Modifier.toString(field.getModifiers()));
}
Field Name - breed Type Name - class java.lang.String Modifier Name - public
In [14]:
Field[] fields = eagle.getDeclaredFields(); // return private & public fields
int z = 0;
for (Field field: fields) {
System.out.println("Field Name - " + field.getName());
System.out.println("Type Name - " + field.getType());
System.out.println("Modifier Name - " + Modifier.toString(field.getModifiers()));
z += 1;
if (z < methods.length) {
System.out.println("------------");
}
}
Field Name - breed Type Name - class java.lang.String Modifier Name - public ------------ Field Name - canSwim Type Name - boolean Modifier Name - private
Setting the Value of a Public Field¶
In [15]:
Class eagle = Eagle.class;
Eagle eagleObj = new Eagle();
Field field = eagle.getDeclaredField("breed");
field.set(eagleObj, "Brown Breed");
eagleObj.breed;
Out[15]:
Brown Breed
Setting the Value of a Private Field¶
In [16]:
// it works in .java file
Field field = eagle.getDeclaredField("canSwim"); // since it is private field, can't be accessible outside of Eagle class
field.set(eagleObj, true);
eagleObj.canSwim;
--------------------------------------------------------------------------- java.lang.IllegalAccessException: class REPL.$JShell$45 cannot access a member of class REPL.$JShell$20$Eagle with modifiers "private" at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:394) at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:714) at java.base/java.lang.reflect.Field.checkAccess(Field.java:1156) at java.base/java.lang.reflect.Field.set(Field.java:833) at .(#45:2)
In [17]:
// correct way to access and set value
Field field = eagle.getDeclaredField("canSwim");
field.setAccessible(true); // it gives access private fields
field.set(eagleObj, true);
if (field.getBoolean(eagleObj)) {
System.out.println("Value is set to true");
}
Value is set to true
Reflection on Constructor¶
In [18]:
import java.lang.reflect.Constructor;
public class Eagle3 {
private Eagle3() {}
public void fly() {
System.out.println("fly");
}
}
In [19]:
Class eagle = Eagle3.class;
// get all public & private constructors
Constructor[] eagleConstructorList = eagle.getDeclaredConstructors();
for (Constructor e: eagleConstructorList) {
System.out.println("Modifier: "+ Modifier.toString(e.getModifiers()));
e.setAccessible(true);
Eagle3 eagle_ = (Eagle3) e.newInstance();
eagle_.fly();
}
Modifier: private fly