The Reflection API

Binary code on blue background with a magnifying glass held over it.

We use the Reflection API to find out about the internals of a Java class — its fields, constructors, methods and annotations. We can also use it to programmatically instantiate objects, invoke methods and access fields at runtime.

This is a very powerful API. It is used extensively by JEE containers, the Spring framework, GUI builders and other APIs.

Heart of the Reflection API

The heart of the Reflection API is the java.lang.Class class. It allows us to load classes into memory at runtime.

The java.lang.Class class includes methods to load classes by name, get references to all or specific constructors, methods and fields, get the annotations on all these elements, and test whether an object is a primitive, an enum, an array, etc.

Reflection Classes

The classes FieldConstructor and Method are in the java.lang.reflect package. The Annotation interface is in the java.lang.annotation package.

Each class provides information about, and access to, that member. For example, the Method class has an invoke() method that can be used to invoke the method on a specific object. The Field class has a variety of get() and set() methods that can read and write to the fields of a specific object. It’s possible to access restricted members in ways that aren’t directly allowed by the compiler.

Each of the previous classes (ClassFieldConstructor and Method) implement the AnnotatedElement interface. This interface represents an annotated element running in the JVM and allows annotations to be read reflectively. This allows us to access any annotations with RUNTIME retention.

Example 1

Here’s a simple application that programmatically inspects the contents of any class (exception handling is not shown):

import java.lang.reflect.*;

public class ReflectionTester {

    public static void main (String args[]) {  
        // Load a class using name from the command line.

        Class<?> cl = Class.forName(args[0]);

        System.out.println ("----- FIELDS -----");
        Field fld[] = cl.getFields();
        for (int i = 0; i < fld.length; i++)
            System.out.println (fld[i]);

        System.out.println ("----- CONSTRUCTORS -----");
        Constructor con[] = cl.getConstructors();
        for (int i = 0; i < con.length; i++)
            System.out.println (con[i]);

        System.out.println ("----- METHODS -----");
        Method met[] = cl.getMethods();
        for (int i = 0; i < met.length; i++)
            System.out.println (met[i]);
    }
}

We would run the application from the console as follows:

java ReflectionTester SomeClassName

Example 2

But that’s not all, folks! The previous example just printed out the elements of the class. We can also instantiate objects by calling appropriate constructors, invoke their methods and modify fields (even if they are private!).

To create an object on the fly at runtime, the class Class has a newInstance() method which invokes the default no-argument constructor of that class. To call a specific constructor, the Constructor class has a newInstance(Object ... initParameters) method which invokes the appropriate constructor with the specified parameters.

Let’s assume we have a class with a constructor taking a String parameter. We can instantiate an object of that class by calling that constructor and passing in a String. The code below assumes that the String value is also passed in at the command line as follows:

java ReflectionTester SomeClassName SomeString

We will then call the instantiated object’s toString() and hashCode() method (remember that all classes inherit those methods from the Object class).

// Load a class using the name provided on the command line. 
Class<?> cl = Class.forName(args[0]);

// Instantiate an object using the specified constructor 
Constructor<?> c = cl.getConstructor(
                  new Class<?>[]{java.lang.String.class});
Object ob = c.newInstance(new Object[]{args[1]});

// Get a reference to the toString() method and invoke it 
// The ob parameter is essentially the "this" variable
Method m = cl.getMethod("toString", new Class<?>[]{});
System.out.println(m.invoke(ob, new Object[]{})); 

// Get a reference to the hashCode() method and invoke it 
m = cl.getMethod("hashCode", new Class<?>[]{});

System.out.println(m.invoke(ob, new Object[]{}));

Conclusion

The Reflection API might look intimidating, but we don’t have to worry too much about its complexity. It’s used mostly by developers of containers.

But we do need to keep reflection in mind when we write classes. We must follow standard naming conventions, so that object properties can be automatically discovered by the container at runtime. (You can read my post on JavaBeans and POJOs, as well as my post on canonical classes.)

Don’t forget to sign up to get our weekly Java tips.

Leave a Comment

Your email address will not be published. Required fields are marked *