For every type of object, the Java virtual machine instantiates an immutable instance of java.lang.Class which provides methods to examine the runtime properties of the object including its members and type information. Class also provides the ability to create new classes and objects. Most importantly, it is the entry point for all of the Reflection APIs. [1]
The entry point for all reflection operations is java.lang.Class:
There are a number of other methods that return Class:
Class.getSuperclass()
Class.getClasses()
Class.getDeclaredClasses()
Class.getDeclaringClass()
Class.getEnclosingClass()
Once we have a class, we can extract a lot of information about it, such as modifiers, type parameters, implemented interfaces, annotations etc…
Getting Fields, Methods, and Constructors
getDeclaredFields() returns a list of all Fields that the class has (i.e. public, protected, and private), while getFields() returns a list of all publicly accessible Fields that the class has (i.e. public, and public inherited fields).
The same pattern can be used to get Methods, and Constructors.
Code:
Output:
-- getDeclaredFields() --
private java.lang.String ReflectionTest$1InheritedClass.privateField
protected java.lang.String ReflectionTest$1InheritedClass.protectedField
public java.lang.String ReflectionTest$1InheritedClass.publicField
-- getFields() --
public java.lang.String ReflectionTest$1InheritedClass.publicField
public java.lang.String ReflectionTest$1SimpleClass.inheritedPublicField
Modifying Fields
Code:
Output:
public original
private original
public changed
private changed
Calling Methods
Code:
Output:
Invoking 'printOut' with parameter Hello World
3
Calling Constructors
Code:
Output:
Invoked public no arg constructor
Invoked private constructor with arg: hello world
Reflection in the wild
One good example of reflection is in JUnit 4 which uses reflection to identify which methods in the test class have the @Test annotation.
Here is an code example of a (failing) JUnit test. We first pass the class to JUnitCore to be run; unless we have specified a JUnitRunner (with the @RunWith) annotation, JUnit will use the default class runner. The class runner instanciates a TestClass which identifies which if the methods have the @Test, and should be run as a test.