Reflection

We all know, all Java classes are subclasses of a special class called Object. Let’s look at one special method inside the Object class.

public class Object {
    public final native Class<?> getClass();
}

You may think, the return type Class is the actual class that we write with the .java extension. But that is not correct. Class can not exist at runtime. Only objects created from classes can exist. Now the question is what is this return type then?

This is one special class and instances of this class represent classes and interfaces at runtime. By represents we mean, it provides some metadata about the class like –

  • name
  • package
  • methods
  • fields
  • constructors
  • annotations

There is a Class object for every class and this Class object is responsible for producing instances for its class.

Let’s look at the source code of Class class to get a better idea. This is not the complete source code. We are showing a few methods to give you an idea how it looks.

public final class Class<T> {
    public static Class<?> forName(String className) { ... }
    public T newInstance() { ... }
    public Field[] getFields() { ... }
    public Method[] getMethods() { ... }
    public Annotation[] getDeclaredAnnotations() { ... }
    // Many others
}
Get the Class object

Before getting into this, first create two classes that will be used throughout this course.

package test.demo;

public class Vehicle {
    private String color;

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    private void parentPrivateMethod() {
        System.out.println("Parent class private method");
    }
}
package test.demo;

public class FourWheeler extends Vehicle {
    private int wheelCount;
    
    public FourWheeler() {}
    
    public FourWheeler(int wheelCount) {
        this.wheelCount = wheelCount;
    }

    public int getWheelCount() {
        return wheelCount;
    }

    public void setWheelCount(int wheelCount) {
        this.wheelCount = wheelCount;
    }

    private void childPrivateMethod() {
        System.out.println("Child class private method");
    }
}

We have created one parent class Vehicle and another child class FourWheeler that extends Vehicle.

Coming back to the question, how to get the Class object. One option we already know, use the getClass() method of Object class.

FourWheeler vehicle = new FourWheeler();
Class<?> vehicleClass = vehicle.getClass();

We can also get the Class object using the .class notation. You can consider class as a special public, static, final field that exists in every class.

Class<?> vehicleClass = FourWheeler.class;

We can also use the forName() static method of class Class that accepts the fully qualified class name as a String and returns the Class object.

Class<?> vehicleClass = Class.forName("test.demo.FourWheeler");
Field and Method class

We have seen from the Class source code, the getFields() method returns an array of Field and getMethods() returns an array of Method.

public final class Class<T> {
    public Field[] getFields() { ... }
    public Method[] getMethods() { ... }
}

Just like an instance of class Class represents a class at runtime, instances of Field and Method represent fields and methods at runtime.

Field class can be used to dynamically get or set value to the field it represents. Similarly, Method class can be used to invoke a method dynamically at runtime.

What is reflection

Reflection is a special feature in the Java programming language that allows you to examine or introspect a class and call attributes, methods etc. dynamically at runtime.

When you use reflection, you follow 3 steps –

  1. Obtain the java.lang.Class object.
  2. Get the Methods or Fields from the Class object.
  3. Use the Field to get or set a field value or use Method to invoke the method it represents.

We’ll see this in action in a while. But before that let’s understand why it is useful.

Why reflection?

Java Reflection makes it possible to inspect and instantiate classes at runtime without knowing the names of the classes at compile time. For example, in the web.xml file we specify the list of servlets. At runtime, the servlet container processes the web.xml file and creates a new instance of each servlet class using reflection. So, reflection enables us to just declare the servlet configuration. The actual instantiation or calling a servlet method happens at runtime using reflection.

Another very common use case is the usage with annotations. For example, when we write a test case in JUnit, we annotate test methods with @Test annotation. We don’t write the code to actually invoke them. JUnit uses reflection to inspect our classes for methods tagged with the @Test annotation, and will then call them when running the unit test.

Creating New Object

Let’s assume you want to write a generic factory method that will accept a class name as String and will instance and return an instance of that type. We can use reflection to achieve that.

public class TestJava {
    public static void main(String[] args) throws Exception {
        FourWheeler vehicle = getObject("test.demo.FourWheeler");
    }
    
    public static <T> T getObject(String className) throws Exception {
        Class<?> clazz = Class.forName(className);
        return (T) clazz.newInstance();
    }
}

If you want to support constructor with parameter, you have to write something like below –

public class TestJava {
    public static void main(String[] args) throws Exception {
        FourWheeler vehicle1 = getObject("test.demo.FourWheeler");
        FourWheeler vehicle2 = getObject("test.demo.FourWheeler", new Class<?>[]{int.class}, new Object[]{5});
        System.out.println(vehicle2);
    }
    
    public static <T> T getObject(String className) throws Exception {
        return getObject(className, null, null);
    }
    
    public static <T> T getObject(String className, Class<?>[] paramType, Object[] argList) throws Exception {
        Class<?> clazz = Class.forName(className);
        T obj = null;
        if (paramType == null) {
            obj = (T) clazz.newInstance();
        } else {
            Constructor constructor = clazz.getConstructor(paramType);
            obj = (T) constructor.newInstance(argList);
        }
        return obj;
    }
}
Get Class hierarchy

We all know how to get the Class object. From that Class object, we can also get the Class instance of the super class using getSuperclass() method. So, we can recursively call this getSuperclass() and get the class hierarchy.

FourWheeler vehicle = new FourWheeler();
Class<?> clazz = vehicle.getClass();

while(clazz != null) {
    System.out.println("Class : " + clazz.getName());
    clazz = clazz.getSuperclass();
}

Output:

Class : test.demo.FourWheeler
Class : test.demo.Vehicle
Class : java.lang.Object
Get methods of a class
FourWheeler vehicle = new FourWheeler();
Class<?> clazz = vehicle.getClass();
Method[] declaredMethods = clazz.getDeclaredMethods();

for (Method method : declaredMethods) {
    System.out.println("Method : " + method.getName());
}

Output:

Method : childPrivateMethod
Method : getWheelCount
Method : setWheelCount

If you want to include methods from parent classes, you have to use getMethods() instead of getDeclaredMethods().

FourWheeler vehicle = new FourWheeler();
Class<?> clazz = vehicle.getClass();
Method[] declaredMethods = clazz.getMethods();

for (Method method : declaredMethods) {
    System.out.println("Method : " + method.getName());
}

Output:

Method : setWheelCount
Method : getWheelCount
Method : setColor
Method : getColor
… all object class public methods …

As you may have noticed, getDeclaredMethods() includes all methods declared by the class itself including private methods. Whereas getMethods() returns only public methods including the inherited methods.

We can also get a method by name. If a method accepts arguments, you have to specify those argument types.

Class<?> clazz = vehicle.getClass();
Method  getterMethod = clazz.getDeclaredMethod("getWheelCount");
Method  setterMethod = clazz.getDeclaredMethod("setWheelCount", int.class);
Method  privateMethod = clazz.getDeclaredMethod("childPrivateMethod");
Invoking Method

We can also use the Method object to invoke the method it represents. To do this, we have to call the invoke() method and provide the object on which the method will be invoked and the arguments if this method requires arguments.

FourWheeler vehicle = new FourWheeler();
Class<?> clazz = vehicle.getClass();
Method  getterMethod = clazz.getDeclaredMethod("getWheelCount");
Method  setterMethod = clazz.getDeclaredMethod("setWheelCount", int.class);

setterMethod.invoke(vehicle, 3);
int value = (int) getterMethod.invoke(vehicle);

System.out.println( value); // 3

Now try to call the private method –

FourWheeler vehicle = new FourWheeler();
Class<?> clazz = vehicle.getClass();
Method  privateMethod = clazz.getDeclaredMethod("childPrivateMethod");
privateMethod.invoke(vehicle);

You will get an exception –

Exception in thread "main" java.lang.IllegalAccessException: Class test.demo.TestJava can not access a member of class test.demo.FourWheeler with modifiers "private"

The message is very clear. As we are trying to invoke a private method, it’s throwing IllegalAccessException.

It is also allowed to make a private method accessible using setAccessible() method –

FourWheeler vehicle = new FourWheeler();
Class<?> clazz = vehicle.getClass();
Method  privateMethod = clazz.getDeclaredMethod("childPrivateMethod");
privateMethod.setAccessible(true);
privateMethod.invoke(vehicle);
Get Fields of a class

Getting Fields is similar to getting Methods. If you want to get all fields declared by a class itself including private fields, use the getDeclaredFields() method.

FourWheeler vehicle = new FourWheeler();
Class<?> clazz = vehicle.getClass();
Field[] fields = clazz.getDeclaredFields();
System.out.println(Arrays.toString(fields));

Output:

[private int test.demo.FourWheeler.wheelCount]

If you want to include only public fields including public inherited fields from parent classes, you have to use the getFields() method instead of getDeclaredFields().

Field[] fields = clazz.getFields()

We can also get a field by name.

Field field = clazz.getDeclaredField("wheelCount");
Get and change value of a Field

We can use the Field object to get or change the value of the field it represents. To get the value, we have to call the get() method and provide the object on which the method will be invoked. Also, if the field is private, make the field accessible before invoking the get() method.

FourWheeler vehicle = new FourWheeler(5);
Class<?> clazz = vehicle.getClass();
Field field = clazz.getDeclaredField("wheelCount");
field.setAccessible(true);
int value = (int) field.get(vehicle); // 5

To set a value, use set() method –

FourWheeler vehicle = new FourWheeler(5);
Class<?> clazz = vehicle.getClass();
Field field = clazz.getDeclaredField("wheelCount");
field.setAccessible(true);
field.set(vehicle, 7);

Please note, if the field is static, you can pass null instead of the object (vehicle) while getting or setting the value as static members belong to class, not to a particular instance of that class.

Class<?> clazz = FourWheeler.class;
Field field = clazz.getDeclaredField("<static_field_name>");
field.setAccessible(true);
field.get(null);
field.set(null, <some_value>);

That’s it for now. Hope you have enjoyed this tutorial. If you have any doubt, please ask in the comment section. I will try to answer that as soon as possible. Till then, bye bye.