Home > Articles > Programming > Java

  • Print
  • + Share This
This chapter is from the book

3.2 Reflection

The Java Reflection API presents a Java interface to the metadata contained in the binary class format. You can use reflection to dynamically discover the characteristics of a Java class: its base class, superinterfaces, method signatures, and field types. Better yet, you can use reflection to dynamically instantiate objects, invoke methods, and mutate fields. These features make it possible to write generic object services that do not rely on, or even have advance knowledge of, the specific classes they will be working on.

Reflection makes it straightforward to serialize an instance to a stream, generate relational database tables that correspond to a class's fields, or create a user interface that can manipulate instances of any arbitrary class. Reflection can also be used to automatically generate source or compiled code that forwards method calls for a set of interfaces to a generic handler. This feature is invaluable for adding layers of code for logging, auditing, or security. Reflection allows you to write service layers that do not require compile-time knowledge of the specific systems they will support.

Some examples of reflection in the core API include serialization and JavaBeans. Java can serialize class instances by writing their state into an opaque stream to be reloaded in some other time or place. Java serialization works even for classes that have not been written yet, because the serialization API uses reflection to access class fields.

At the bare minimum, a JavaBean is a serializable class with a default constructor. However, bean-aware tools can use reflection to discover the properties and events associated with a bean. This means that tools can deal with classes that they have never seen simply by reflecting against them.

Serialization and JavaBeans are powerful idioms, but they are still just idioms. Their underlying architecture is reflection. If you understand reflection you can develop your own idioms, more suited to your particular problem domain.

Most of the Reflection API lives in the java.lang.reflect package, but the central class in reflection is the java.lang.Class class. A Class represents a single binary class, loaded by a particular class loader, within the virtual machine. By using a Class instance as a starting point, you can discover all type information about a class. For example, you might want to know all of a class's superclasses and superinterfaces. The ListBaseTypes class shown in Listing 3–6 uses the Class methods getInterfaces and getSuperclass to return a class's superinterfaces and superclass, and then it follows these recursively back to the beginning, which is defined to be java.lang.Object for classes and null for interfaces. Sample output for ListBaseTypes is shown in Listing 3–7.

Listing 3–6 The ListBaseTypes Class

public class ListBaseTypes {
 public static void main(String [] args) throws Exception
 {
  Class cls = Class.forName(args[0]);
  System.out.println("Base types for " + args[0]);
  listBaseTypes(cls, "");
 }
 public static void listBaseTypes(Class cls, String pref) {
  if (cls == Object.class) return;
  Class[] itfs = cls.getInterfaces();
  for (int n=0; n<itfs.length; n++) {
   System.out.println(pref + "implements " + itfs[n]);
   listBaseTypes(itfs[n], pref+"\t");
  }
  Class base = cls.getSuperclass();
  if (base == null) return;
  System.out.println(pref + "extends " + base);
  listBaseTypes(base, pref+"\t");
 }
}

Listing 3–7 Sample Output from ListBaseTypes

Base types for java.io.ObjectOutputStream
implements interface java.io.ObjectOutput
	implements interface java.io.DataOutput
implements interface java.io.ObjectStreamConstants
extends class java.io.OutputStream
	extends class java.lang.Object

3.2.1 Reflecting on Fields

A more interesting use of reflection is to discover the fields of a class. Fields are represented by the java.lang.reflect.Field class. As Listing 3–8 shows, the Field class contains all of the information from the original source code declaration of a field: the field's name, type, and modifiers. The Class class provides several methods for retrieving a class's fields, shown in Listing 3–9.

Listing 3–8 Type Information in the Field Class

package java.lang.reflect;
public class Field {
 public String getName();
 public int getModifiers();
 public Class getType();
}//remainder omitted for clarity

Listing 3–9 Class Methods for Accessing Fields

package java.lang;
public class Class {
 public Field[] getDeclaredFields();
 public Field[] getFields();
 public Field getDeclaredField(String name)
  throws NoSuchFieldException;
 public Field getField(String name)
  throws NoSuchFieldException;
 //remainder omitted for clarity
}

3.2.2 The Difference between get and getDeclared

The Field methods of Class exhibit a pattern that occurs throughout the Reflection API: an accessor method named getXXX, plus a similar accessor named getDeclaredXXX. These method forms differ in two ways. The getXXX methods return only public members of a class, but they will return members from a class and all of its base classes. The getDeclaredXXX methods will return all members of a class, regardless of protection modifier; however, they will not recurse to base classes. There is no compelling reason for or against this convention; you simply must memorize it. The combination of getFields and getDeclaredFields is not sufficient to access nonpublic base class fields. As shown in Figure 3–2, in order to list all fields of a class and its base classes, you need to use the getDeclaredField form and then also recurse to base classes using getSuperclass.

Figure 3–2 The difference between get and getDeclared

If you run ListMostFields (Listing 3–10) against String, you will see a list of nine fields (on the Java 2 SDK1.3, anyway). This surprisingly large list is due to the fact that the field APIs return static fields as well as instance fields. In order to distinguish static from instance fields, or to discover any other field modifiers, you must call getModifiers.

Listing 3–10 ListMostFields

import java.lang.reflect.*;
/**
 * lists all fields except superinterface fields
 * and fields from Object 
 */
 public class ListMostFields {
 public static void main(String [] args) throws Exception
 {
  Class cls = Class.forName(args[0]);
  System.out.println("Fields for " + args[0]);
  listMostFields(cls);
 }
 public static void listMostFields(Class cls) {
  if (cls == Object.class) return;
  Field[] fields = cls.getDeclaredFields();
  for (int n=0; n<fields.length; n++) {
   System.out.println(fields[n]);
  }
  Class base = cls.getSuperclass();
  if (base == null) return;
  System.out.println("extends " + base);
  listMostFields(base);
 }
}

The getModifiers call is one of the few places where Java thoroughly shows its nuts-and-bolts C language heritage. Instead of returning a Modifiers object, getModifiers returns an int that is a collection of bit flags. There actually is a java.lang.reflect.Modifiers class, but all it does is provide constants and static methods to interpret the bit flags. So, you can determine whether a Field is static by calling a static method on the Modifiers class but not through an instance of Field itself, as shown here:

//this is correct
Modifiers.isStatic(f.getModifiers()); 

//These are intuitive possibilities, but will not compile!
f.isStatic();
f.getModifiers().hasStatic();

3.2.3 Type Errors Occur at Runtime

The getField and getDeclaredFields of Class request a particular Field by name. These APIs introduce another wrinkle that recurs throughout reflection—the possibility that any call may fail at runtime. With "normal" nonreflective code, you get substantial feedback at compile time. If you reference a field that does not exist, the compiler will simply refuse to compile the class. With reflection, you are querying for the existence of a field at runtime, and you must be prepared for the possibility of failure at runtime. The getField and getDeclaredField methods remind you of this possibility by throwing the checked exception NoSuchFieldException. This is the blessing and curse of reflective programming. You can use reflection to refer to classes that do not even exist at compile time, but you must remember that the compiler is powerless to protect you from misusing such classes.

Listing 3–11 Type Information in the Method and Constructor Classes

//these methods appear in both
//java.lang.reflect.Method and 
//java.lang.reflect.Constructor
public String getName();
public int getModifiers();
public Class getReturnType();
public Class[] getParameterTypes();
public Class[] getExceptionTypes();

3.2.4 Reflecting on Methods

You can use reflection to access methods in a fashion very similar to accessing fields. Methods are represented by the java.lang.reflect.Method class, which preserves the information from the method signature in the original Java source code file, as shown in Listing 3–11. Finding Method objects is slightly more complex than finding Field objects because Method names can be overloaded. The Class APIs for Methods, shown in Listing 3–12, require that you specify argument types in addition to the method name. In addition to argument types, the Method class also lets you access the return type and checked exceptions thrown by a method.

Listing 3–12 Class Methods for Accessing Methods and Constructors

//java.lang.Class methods for accessing Methods
//versions that take a name throw NoSuchMethodException
public Method getDeclaredMethod(String name, Class[]                
  parameterTypes);
public Method getMethod(String name, 
            Class[] parameterTypes);
 public Method[] getDeclaredMethods();
public Method[] getMethods();
//methods for accessing Constructors
public Constructor getDeclaredConstructor(Class[]          
   parameterTypes);
public Constructor getMethod(Class[] parameterTypes);
public Constructor[] getDeclaredConstructors();
public Constructor[] getConstructors();

The rules for Methods are exactly the same as for Fields—the getDeclared versions of the APIs ignore protection modifiers, while the get versions return public members for the class and its base classes. The Constructor APIs work almost exactly like the Method APIs, but since the name of a constructor is implicit, the name parameter is absent from the Constructor-related _methods.

  • + Share This
  • 🔖 Save To Your Account