Session 19|Java Reflection¶

What is Reflection?¶

  • Java Reflection Example Tutorial
  • Guide to Java 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 Class class is an instance that represents a class during runtime.
  • The JVM creates one Class object for each class loaded at runtime.
  • This Class object 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