| ## Java 1.1 Unleashed # - 35 - Java Reflection by Mike Fletcher IN THIS CHAPTER An Overview of the Reflection Package A Reflection Example This chapter covers the new Reflection API introduced in Java 1.1. The `java.lang.reflect` package contains classes that allow Java code to examine classes and interfaces dynamically. After a quick overview of the API (refer to Chapter 18, "The Reflection Package," for more detailed coverage of the package), this chapter presents a sample applet that uses the reflection classes to print out information about an arbitrary class. ## An Overview of the Reflection Package The Reflection API allows Java code to examine classes and objects at run time. The new reflection classes allow you to call another class's methods dynamically at run time. With the reflection classes, you can also examine an instance's fields and change the fields' contents. The reflection classes also enable you to create new class and array instances on the fly. All this new functionality is, of course, limited by the Java security model and the Java language access specifiers (for example, `private` and `protected`). The new Reflection API is intended to provide the functionality required by the JavaBeans package to access the `public` methods and fields of arbitrary classes at run time. The Reflection API also provides applications such as debuggers with access to the declared members of a class. In addition to the new reflection classes, some additions have been made to the `java.lang` package. `Byte` and `Short` classes, analogous to the `Integer` class, have been added to serve as wrappers for the corresponding primitive types. New instances of the `Class` class, named `java.lang.`Type`.TYPE` (where Type is `Boolean`, `Character`, `Byte`, `Short`, `Integer`, `Long`, `Float`, `Double`, or `Void`), have been added to represent primitive field, parameter, and return value types. ### The java.lang.reflect API The Reflection API consists of the `java.lang.Class` class and the `java.lang.reflect` classes: `Field`, `Method`, `Constructor`, `Array`, and `Modifier`. The first three of these classes represent the corresponding members of classes and interfaces. The `Array` class provides methods for creating, accessing, and modifying arrays. The last class provides methods for decoding modifiers (such as `static` and `public`) returned for classes and their components. All these classes allow widening conversions when appropriate (for example, an `int` will be converted to a `long`), but throw an `IllegalArgumentException` if the value has to be narrowed (for example, the classes do not convert a `double` to a `float`). #### The Class Class The `Class` class, which remains in the `java.lang` package for backwards compatibility, provides running code with a representation of Java types. In Java 1.1, the `Class` class provides the same functionality as it did in Java 1.0, with the following additions: Methods to determine whether an object represents an array or a primitive type. Methods that return a representation of the modifiers for the class type. Methods that return objects representing the fields, methods, and constructors, as well as the component type for arrays. Two versions are available for each type: One returns only the members declared specifically in the class, and the other returns all members of the class, including inherited members. Methods that determine whether a class (or interface) is a superclass of a given class (or interface), as well as methods that determine whether an object is an instance of a type or implements an interface. There are no `public` constructors for the `Class` class. The virtual machine constructs `Class` objects when classes are loaded. References to a `Class` object for a given class can be obtained with the `forName()` method. #### The Member Interface The `Member` interface defines shared functionality implemented by the `Field`, `Method`, and `Constructor` classes. It defines methods to get information such as the name, declaring class, and modifiers for type members. Two static constants, `PUBLIC` and `DECLARED`, are used by the security manager to determine whether code can access members. #### The Field Class `Field` instances give access to one field of a type. A `Field` object is created by the virtual machine when the `getField()`, `getFields()`, `getDeclaredField()`, or `getDeclaredFields()` method of `Class` is called. Both instance variables and class (`static`) variables can be represented by a `Field` object. In addition to the methods declared by the `Member` interface, the `Field` class defines methods to retrieve and set the contents of a field. A generic `get()` method is provided that takes an `Object` reference as a parameter and returns an `Object` representing the value of the field. Specific methods are provided to return a value of the appropriate primitive type (or throw an `IllegalArgumentException` if the value represented by the `Field` object is not of that type). Similarly, a generic `set()` method and other type-specific methods are provided to set the contents of the field. #### The Method Class Just as the `Field` class represents the fields of a type, the `Method` class represents the methods of a class. References to `Method` objects can be obtained with the `getMethod()`, `getDeclaredMethod()`,`getMethods()`, and `getDeclaredMethods()` members of the `Class` class. The `Method` class implements the `Member` interface. The `Method` class provides methods to determine the return type, parameters, and exceptions thrown by the represented method. These are returned as references to `Class` objects for the first two methods, and arrays of `Class` objects for the last two methods. The `invoke()` method provides a way to call the method represented by the `Method` object. It takes as parameters the `Object` on which to call the method and an `Object` array representing the parameters. For class methods, the first parameter can be omitted. If you have problems with the arguments (for example, the first `Object` is not of the correct type, or one of the parameters is not the correct type), an `IllegalArgumentException` is thrown. If an exception is thrown by the method, it is caught and an `InvocationTargetException` is thrown with the original exception (which can be retrieved with the `getTargetException()` method). #### The Constructor Class The `Constructor` class represents a constructor for a class. A `Constructor` instance can be used to create a new instantiation of the declaring class. As with the `Method` and `Field` classes, instances can be created only by the virtual machine and are retrieved with the `Class` class's `getConstructor()` and related methods. Methods are provided to return the parameter types and exceptions thrown by the constructor. A new instance can be obtained by calling the `newInstance()` method. As does the `Method.invoke()` method, the `newInstance()` method throws an `IllegalArgumentException` if there is a problem with the type of a parameter. An `InvocationTargetException` is thrown if the constructor throws an exception. #### The Array Class The `Array` class provides a way of dynamically creating new arrays at run time. Two methods, both with the name `newInstance()`, return a new array with a component type specified by the first parameter. One version takes a single `int` as the second parameter to specify the length of the array, the other takes an `int[]` for creating multidimensional arrays. A `getLength()` method takes an array reference as a parameter and returns the length of the array. Methods similar to those of the `Field` class are provided by the `Array` class to get and set array components. The `get()` method as well as the type-specific versions of `get()` take an array reference and the index to retrieve as parameters. The `set()` method and its type-specific friends take an additional argument, which is the value to set the element. #### The Modifier Class The `Modifier` class is a convenience class that decodes the Java language modifiers returned by the other Reflection API classes. All the `Modifier` methods are static. Classes that implement the `Member` interface define a `getModifiers()` method, which returns an `int`. This integer can be passed to the methods of the `Modifier` class to determine which modifiers are present. Methods such as `isPublic()` are provided for all the Java modifiers: `public`, `private`, `protected`, `static`, `final`, `synchronized`, `volatile`, `transient`, `native`, `interface`, and `abstract`. A `toString()` method is also provided; it returns a space-separated list of the modifiers represented by the `int` parameter. #### The InvocationTargetException Exception The `InvocationTargetException` exception is used to represent an exception thrown by an underlying method or constructor called by the `Method.invoke()` or `Constructor.newInstance()` method. Either of these methods catches any exceptions thrown when the actual code is called. If an exception does occur, an `InvocationTargetException` is created with the exception that occurred, and this new exception is thrown. Two constructors are provided for this exception: One takes only an object extending `java.lang.Throwable`, and the other takes a `Throwable` and a message. ### Security Concerns Access to classes using the Reflection API is controlled by the security manager and by the standard Java language access controls. Before an instance representing a member (`Field`, `Method`, or `Constructor`) is returned, the `SecurityManager.checkMemberAccess()` method is called. If this check does not throw a `SecurityException`, access is granted, with the caveat of the standard checks for `private` and `protected` done by the virtual machine. The access policy for applications is that code can access members for any class to which it can link within the standard language constraints. The JDK `AppletSecurity` class allows public members of public classes to access the public members of code loaded by the same loader; it also allows access to public system classes (classes that loaded from local disk somewhere in the `CLASSPATH`); the `AppletSecurity` class also allows access to nonpublic members of classes loaded by the same class loader. Code that has been signed by a trusted entity can access all members of system classes. In all cases, standard Java access controls are applied. For example, even though code can get a reference to a protected field, the code cannot get or set the value; it can only find out information about the member such as its declaring class and its name. ## A Reflection Example To show how the Reflection API can be used, this part of the chapter develops a simple applet that displays information about a given class. The `classInfo` constructor takes two parameters: the `Class` to print the information to, and a `java.awt.TextArea` to display the information. A command-line version can easily be created by replacing the `TextArea` with a `PrintWriter` and converting the `appendText()` calls to `print()`. The final code for the finished `classInfo` example and a driver applet that shows how to use it is located on the accompanying CD-ROM. Let's start with the overall skeleton for the class. Listing 35.1 shows the constructor and a convenience method called `smartTypePrint()`. The constructor copies its parameters into the corresponding instance variables. The `smartTypePrint()` method determines whether the `Class` parameter represents an array type and appends `[]` to the name if appropriate. The last three methods shown in Listing 35.1 are fleshed out in the next sections. #### Listing 35.1. The classInfo skeleton.
    import java.lang.reflect.*;
    import java.io.*;
    public class classInfo {
      protected Class myClass;
      protected java.awt.TextArea os;
      // Constructor
      public classInfo( Class c, java.awt.TextArea a ) {
        myClass = c;
        os = a;
      }
      public String smartTypePrint( Class c ) {
        if( c.isArray( ) ) {
          return new String( smartTypePrint( c.getComponentType() ) + "[]" );
        } else {
          return c.getName();
        }
      }
      public void generalInfo() { ... }
      public void methodInfo() { ... }
      public void fieldInfo() { ... }
    }
### Class Name, Superclass, and Other Information The `generalInfo()` method prints out information about the class in question. The general information it prints includes the class's name, whether it is a class or an interface, and any modifiers. Listing 35.2 shows the `generalInfo()` method. #### Listing 35.2. The generalInfo() method.
    public void generalInfo( ) {
        os.appendText( "Name: " + myClass.getName( ) + "\n" );
        os.appendText( "Class/Interface: "
                  + (myClass.isInterface() ? "Interface" : "Class") );
        os.appendText( "\tSuperclass: " + myClass.getSuperclass().getName()
                    + "\n" );
        os.appendText( "Primitive type: " + myClass.isPrimitive() + "\n" );
        os.appendText( "Interfaces: " );
        Class interfaces[] = myClass.getInterfaces();
        if( interfaces.length != 0 ) {
          for( int i = 0; i < interfaces.length; i++ ) {
            if( (i % 3) == 4) {
              os.appendText( "\n\t" );
            }
            os.appendText( interfaces[ i ].getName() + " " );
          }
          os.appendText( "\n" );
        } else {
          os.appendText( "None\n" );
        }
        os.appendText( "Class Modifiers: "
                    + Modifier.toString( myClass.getModifiers() ) + "\n" );
      }
### Fields The `fieldInfo()` method prints each of the fields declared by the class (see Listing 35.3). Inherited fields are ignored, although they can be obtained by using the `getFields()` method instead of `getDeclaredFields()`. The modifiers and type of the field are printed, followed by the name of the field. Although the `Field` class provides a `toString()` method that provides the same information, we'll do it ourselves here for the sheer fun of it. #### Listing 35.3. The fieldInfo() method.
    public void fieldInfo( ) {
        Field fields[] = null;
        os.appendText( "Fields:\n" );
        try {
          fields = myClass.getDeclaredFields();
        } catch( SecurityException e ) {
          os.appendText( "Error getting fields: "
                         + e.getMessage() + "\n" );
          return;
        }
        if( fields != null ) {
          if( fields.length != 0 ) {
            for( int i = 0; i < fields.length; i++ ) {
              os.appendText( "\t" + Modifier.toString(
                                      fields[i].getModifiers() ) );
              os.appendText( " " + smartTypePrint( fields[i].getType() ) + " " );
              os.appendText( fields[ i ].getName() + "\n" );
            }
          } else {
            os.appendText( "\tNone\n" );
          }
        }
        os.appendText( "\n" );
      }
### Constructors Next, the `constructorInfo()` method prints out any constructors for the class (see Listing 35.4). The modifiers for each constructor are printed, followed by any parameters the constructor takes. If the constructor throws any exceptions, these are printed next. #### Listing 35.4. The constructorInfo() method.
    public void constructorInfo( ) {
        Constructor constructors[] = null;
        os.appendText( "Constructors:\n" );
        try {
          constructors = myClass.getDeclaredConstructors();
        } catch( SecurityException e ) {
          os.appendText( "Error getting constructors: " + e + "\n" );
          return;
        }
        if( constructors != null ) {
          Class a[] = null;
          if( constructors.length != 0 ) {
            for( int i = 0; i < constructors.length; i++ ) {
              os.appendText( " " + constructors[ i ].getName()
                          + "\n ----------\n" );
              os.appendText( "\tModifiers: " +
                            Modifier.toString( constructors[i].getModifiers() ) + "\n" );
              a = constructors[ i ].getParameterTypes();
              if( a.length != 0 ) {
                os.appendText( "\tParameters (" + a.length + "): " );
                for( int j = 0; j < a.length; j++ ) {
                  if( (j % 8) == 7 ) {
                    os.appendText( "\n\t\t" );
                  }
                  os.appendText( smartTypePrint( a[j] ) + " " );
                }
              } else {
                os.appendText( "\tParameters: none" );
              }
              os.appendText( "\n" );
              a = constructors[ i ].getExceptionTypes();
              if( a.length != 0 ) {
                os.appendText( "\tExceptions (" + a.length + "): " );
                for( int j = 0; j < a.length; j++ ) {
                  if( (j % 8) == 7 ) {
                    os.appendText( "\n\t\t" );
                  }
                  os.appendText( smartTypePrint( a[j] ) + " " );
                }
              } else {
                os.appendText( "\tExceptions: none" );
              }
              os.appendText( "\n\n" );
            }
          } else {
            os.appendText( "None\n" );
          }
        }
    }
### Methods The last method, `methodInfo()`, looks remarkably similar to the `constructorInfo()` method (see Listing 35.5). The `methodInfo()` method also makes use of the `getReturnType()` method to determine the return type of the method in question. #### Listing 35.5. The methodInfo() method.
    public void methodInfo( ) {
        Method methods[] = null;
        os.appendText( "Methods:\n" );
        try {
          methods = myClass.getDeclaredMethods();
        } catch( SecurityException e ) {
          os.appendText( "Error getting methods: " + e + "\n" );
          return;
        }
        if( methods != null ) {
          Class a[] = null;
          if( methods.length != 0 ) {
            for( int i = 0; i < methods.length; i++ ) {
              os.appendText( " " + methods[ i ].getName()
                          + "\n ----------\n" );
              os.appendText( "\tModifiers: " +
                     Modifier.toString( methods[i].getModifiers() ) + "\n" );
              os.appendText( "\tReturns: " +
                     smartTypePrint( methods[i].getReturnType() ) + "\n" );
              a = methods[ i ].getParameterTypes();
              if( a.length != 0 ) {
                os.appendText( "\tParameters (" + a.length + "): " );
                for( int j = 0; j < a.length; j++ ) {
                  if( (j % 8) == 7 ) {
                    os.appendText( "\n\t\t" );
                  }
                  os.appendText( smartTypePrint( a[j] ) + " " );
                }
              } else {
                os.appendText( "\tParameters: none" );
              }
              os.appendText( "\n" );
              a = methods[ i ].getExceptionTypes();
              if( a.length != 0 ) {
                os.appendText( "\tExceptions (" + a.length + "): " );
                for( int j = 0; j < a.length; j++ ) {
                  if( (j % 8) == 7 ) {
                    os.appendText( "\n\t\t" );
                  }
                  os.appendText( smartTypePrint( a[j] ) + " " );
                }
              } else {
                os.appendText( "\tExceptions: none" );
              }
              os.appendText( "\n\n" );
            }
          } else {
            os.appendText( "None\n" );
          }
        }
    }
### User Interface The user interface for the applet is very simple, as shown in Listing 35.6. It consists of a `TextField` to allow the class name to be entered, a `Button` to click to print the information, and the `TextArea` in which to display the information. When the button is clicked, the applet's `action()` method tries to get a `Class` reference for the class named in the field. If it retrieves a `Class`, it creates a `classInfo` object with the `TextArea` for output and calls each of the information methods. Figure 35.1 shows the `classInfoApplet` in action. Figure 35.1. The `classInfoApplet`. #### Listing 35.6. The classInfoApplet class.
    import java.applet.*;
    import java.awt.*;
    public class classInfoApplet
      extends Applet
    {
      TextField className;                // For entering class name
      Button    goButton;                 // Press button to do lookup
      TextArea  infoArea;                 // Area to print output
      // init -- Create GUI.
      public void init( ) {
        setLayout( new BorderLayout( ) );

        Panel p = new Panel();
        p.setLayout( new FlowLayout( ) );
        className = new TextField( 40 );
        p.add( className );
        goButton = new Button( "Print Info" );
        p.add( goButton );
        add( "North", p );
        infoArea = new TextArea( 80, 80 );
        add( "Center", infoArea );
      }
      // action -- Dispatch events
      public boolean action( Event ev, Object arg ) {
        // If the target is our button
        if( ev.target == goButton ) {
          Class c = null;
          String name = className.getText(); // Get class name from field
          // Let user know what we're up to
          showStatus( "Trying class '" + name + "'" );
          // Try and get a Class object for that name
          try {
            c = java.lang.Class.forName( name );
          } catch( ClassNotFoundException e ) {
            // Gripe if class wasn't found
            showStatus( "Couldn't load class: " + e.getMessage() );
            return true;
          }
          if( c == null ) {
            // Paranoia check that we got a valid class object.
            showStatus( "Class is null??" );
          } else {
            infoArea.setText( "" );       // Clear out text area
            // Make a classInfo object with the Class and TextArea
            classInfo ci = new classInfo( c, infoArea );
            // Print information
            ci.generalInfo();
            ci.fieldInfo();
            ci.constructorInfo();
            ci.methodInfo();
            // Let user know we're done.
            showStatus( "Info for '" + name + "' done." );
          }
          return true;                   // Return that we handled event
        }
        // Let superclass handle event otherwise
        return super.action( ev, arg );
      }
    }
## Summary You should now have an understanding of what the Reflection API can do and how to use it. This new API provides useful functionality of interest to those who write utilities such as debuggers and object browsers, and to those who use the JavaBeans facilities.
| ©Copyright, Macmillan Computer Publishing. All rights reserved.