Jump to content

Reflection Overview

75% developed
From Wikibooks, open books for an open world

Navigate Reflection topic: v  d  e )

Reflection is the mechanism by which Java exposes the features of a class during runtime, allowing Java programs to enumerate and access a class' methods, fields, and constructors as objects. In other words, there are object-based mirrors that reflect the Java object model, and you can use these objects to access an object's features using runtime API constructs instead of compile-time language constructs. Each object instance has a getClass() method, inherited from java.lang.Object, which returns an object with the runtime representation of that object's class; this object is an instance of the java.lang.Class, which in turn has methods that return the fields, methods, constructors, superclass, and other properties of that class. You can use these reflection objects to access fields, invoke methods, or instantiate instances, all without having compile-time dependencies on those features. The Java runtime provides the corresponding classes for reflection. Most of the Java classes that support reflection are in the java.lang.reflect package. Reflection is most useful for performing dynamic operations with Java — operations that are not hard-coded into a source program, but that are determined at run time. One of the most important aspects of reflection is dynamic class loading.

Example: Invoking a main method

[edit | edit source]

One way to understand how reflection works is to use reflection to model how the Java Runtime Environment (JRE) loads and executes a class. When you invoke a Java program

Standard input or output Console

java fully-qualified-class-name arg0 ... argn

and pass it command line arguments, the JRE must

  1. put the command line arguments arg0 ... argn into a String[] array
  2. dynamically load the target class named by fully-qualified-class-name
  3. access the public static void main(String[]) method
  4. invoke the main method, passing the string array main String[].

Steps 2, 3, and 4 can be accomplished with Java reflection. Below is an example of loading the Distance class, locating the main method, (see Understanding a Java Program) and invoking it via reflection.

Example Code section 10.1: main() method invocation.
public static void invokeMain()
   throws ClassNotFoundException,
   	ExceptionInInitializerError,
   	IllegalAccessException,
   	IllegalArgumentException,
   	InvocationTargetException,
   	NoSuchMethodException,
   	SecurityException {
   Class<?> distanceClass = Class.forName("Distance");
   String[] points = {"0", "0", "3", "4"};
   Method mainMethod = distanceClass.getMethod("main", String[].class);
   Object result = mainMethod.invoke(null, (Object) points);
}

This code is obviously more complicated than simply calling

Example Code section 10.2: main() method calling.
Distance.main(new String[]{"0", "0", "3", "4"});

However, the main Java runtime does not know about the Distance class. The name of the class to execute is a runtime value. Reflection allows a Java program to work with classes even though the classes are not known when the program was written. Let's explore what the invokeMain method is doing. The first statement at line 9 is an example of dynamic class loading. The forName() method will load a Java class and return an instance of java.lang.Class that results from loading the class. In this case, we are loading the class "Distance" from the default package. We store the class object in the local variable distanceClass; its type is Class<?>. The second statement at line 10 simply creates a String array with the four command line arguments we wish to pass to the main method of the Distance class. The third statement at line 11 performs a reflection operation on the Distance class. The getMethod() method is defined for the Class class. It takes a variable number of parameters: the method name is the first parameter and the remaining parameters are the types of each of main's parameters. The method name is trivial: we want to invoke the main method, so we pass in the name "main". We then add a Class variable for each of the method parameters. main accepts one parameter (String[] args) so we add a single Class element representing the String[]. The getMethod method has a return type of java.lang.reflect.Method; we store the result in a local variable named mainMethod. Finally, we invoke the method by calling the invoke() method of the Method instance. This method's first parameter is the instance to invoke on, and the remaining parameters are for the invokee's parameters. Since we are invoking a static method and not an instance method, we pass null as the instance argument. Since we only have a single parameter we pass it as the second argument. However, we must cast the parameter to Object to indicate that the array is the parameter, and not that the parameters are in the array. See varargs for more details on this.

Example Code section 10.3: invoke() call.
Object result = mainMethod.invoke(null, arguments);

The invoke() method returns an Object that will contain the result that the reflected method returns. In this case, our main method is a void method, so we ignore the return type. Most of the methods in this short invokeMain method may throw various exceptions. The method declares all of them in its signatures. Here is a brief rundown of what might throw an exception:

  • Class.forName(String) will throw ClassNotFoundException, if the named class cannot be located.
  • Class.forName(String) will throw ExceptionInInitializerError, if the class could not be loaded due to the static initializer throwing an exception or a static field's initialization throwing an exception.
  • Class.getMethod(String name, Class parameterTypes[]) will throw
    • NoSuchMethodException, if a matching method is not found, or is not public (use getDeclaredMethod to get a non-public method).
    • SecurityException, if a security manager is installed and calling the method would result in an access violation (for example, the method is in the sun.* package designed for internal use only).
  • Method.invoke(Object instance, Object... arguments) may throw:
    • IllegalAccessException, if this method is invoked in a manner that violates its access modifiers.
    • IllegalArgumentException for various reasons, including
      • passing an instance that does not implement this method.
      • the actual arguments do not match the method's arguments
    • InvocationTargetException, if the underlying method (main in this case) throws an exception.

In addition to these exceptions, there are also errors and runtime exceptions that these methods may throw.


Clipboard

To do:
Add some exercises like the ones in Variables