Richard G Baldwin (512) 223-4758, baldwin@austin.cc.tx.us, http://www2.austin.cc.tx.us/baldwin/

Reflection and the Method Class - I

Java Programming, Lecture Notes # 260, Revised 02/11/98.

Preface

Students in Prof. Baldwin's Advanced Java Programming classes at ACC are responsible for knowing and understanding all of the material in this lesson.

Introduction

This is the first in a series of planned lessons on reflection.

One of the Java features introduced in JDK 1.1 is reflection. According to the documentation for JDK 1.1:
 
"(Reflection) Enables Java code to discover information about the fields, methods and constructors of loaded classes, and to use reflected fields, methods, and constructors to operate on their underlying counterparts on objects, within security restrictions. The API accommodates applications that need access to either the public members of a target object (based on its runtime class) or the members declared by a given class."
Reflection has many potential uses, one of which is to support the design of advanced forms of adapters for use in event processing. This generally requires the ability to discover information about the methods of loaded classes and to invoke methods as a result of that discovery process.

During this course of study, I plan to introduce you to the use of reflection for fields, methods, and constructors, but in the beginning we will emphasize methods.

Subsequent lessons will discuss the other aspects of reflection including the new capabilities for handling arrays.

Reflection, Summary of Classes

There are about eight classes and interfaces involved in reflection, and each of those classes has numerous methods and in some cases fields.

The major classes and interfaces involved in reflection along with their fields and methods are listed below.

A quick review of this material will indicate that a large part of the difficulty in using reflection is simply figuring out which classes and methods to use for what purpose and when to use them.
 

class java.lang.Class 

    Methods 

    • toString, forName, newInstance, isInstance, isAssignableFrom, isInterface, isArray, isPrimitive, getName, getModifiers, getClassLoader, getSuperclass, getInterfaces, getComponentType, getDeclaringClass, getClasses, getFields, getMethods, getConstructors, getField, getMethod, getConstructor, getDeclaredClasses, getDeclaredFields, getDeclaredMethods, getDeclaredConstructors, getDeclaredField, getDeclaredMethod, getDeclaredConstructor 
.
 

interface java.lang.reflect.Member 

    Fields 

    • PUBLIC, DECLARED 

    Methods 

    • getDeclaringClass, getName, getModifiers 
.
 

class java.lang.reflect.Field 

    Methods 

    • getDeclaringClass, getName, getModifiers, getType, equals, hashCode, toString, get, getBoolean, getByte, getChar, getShort, getInt, getLong, getFloat, getDouble, set, setBoolean, setByte, setChar, setShort, setInt, setLong, setFloat, setDouble 
.
 

class java.lang.reflect.Method 

    Methods 

    • getDeclaringClass, getName, getModifiers, getReturnType, getParameterTypes, getExceptionTypes, equals, hashCode, toString, invoke 
.
 

class java.lang.reflect.Constructor 

    Methods 

    • getDeclaringClass, getName, getModifiers, getParameterTypes, getExceptionTypes, equals, hashCode, toString, newInstance 
.
 

class java.lang.reflect.Array 

    Methods 

    • newInstance, newInstance, getLength, get, getBoolean, getByte, getChar, getShort, getInt, getLong, getFloat, getDouble, set, setBoolean, setByte, setChar, setShort, setInt, setLong, setFloat, setDouble 
.
 

class java.lang.reflect.Modifier 

    Fields 

    • PUBLIC, PRIVATE, PROTECTED, STATIC, FINAL, SYNCHRONIZED, VOLATILE, TRANSIENT, NATIVE, INTERFACE, ABSTRACT 

    Methods 

    • isPublic, isPrivate, isProtected, isStatic, isFinal, isSynchronized, isVolatile, isTransient, isNative, isInterface, isAbstract, toString 
.
 

class java.lang.reflect.InvocationTargetException 

    Constructors 

    • InvocationTargetException, InvocationTargetException 

    Methods 

    • getTargetException 
.

Reflection, An Overview

The Core Reflection API supports introspection on classes and objects currently in the Virtual Machine.

Depending on security policy, this API can be used to:

The Core Reflection API defines a new interface named Member and five new classes including the following three:

Member Interface

The Member interface is a member of the package java.lang.reflect. It declares three important methods that are implemented by several of the classes in the API..

Field Class

The Field class is contained in the package java.lang.reflect. It is a public final class that extends the Object class and implements the Member interface. Because it implements the Member interface, it defines the three methods listed above (in addition to others that are not interface methods).

An object of the Field class provides information about, and access to a single field of a class or interface. This may be a class variable or an instance variable.

Field objects can only be created by the Java Virtual Machine.

The Java class named Class is not a new class. However, it was upgraded significantly to support reflection. There are a variety of methods in the Class class that will return a Field object.

Note that a Field permits widening conversions to occur during a set or get operation, but throws an IllegalArgumentException if a narrowing conversion would occur.

Method Class

The Method class is a member of the package java.lang.reflect. It is a public final class that extends the Object class and implements the Member interface.

An object of the Method class provides information about, as well as access to a single method of a class or an interface. The reflected method can be an abstract method, a class method, or an instance method.

Objects of the Method class can only be instantiated by the Java Virtual Machine. There are several methods in the upgraded Class class that return references to Method objects. As mentioned earlier, our first investigation of the reflection API will emphasize the Method class.

As with a Field, a Method permits widening conversions to occur when matching the actual parameters to invoke with the formal parameters of the method being invoked. An IllegalArgumentException will be thrown if a narrowing conversion would occur.

Constructor Class

The Constructor class is a member of the package java.lang.reflect. It is a public final class that extends the Object class and implements the Member interface.

A Constructor object provides information about, and access to a single constructor of a class. The object may be used to create and initialize a new instance of the class that declares the reflected constructor (provided that the class can be instantiated).

The newInstance() method of the Constructor class is used to instantiate new instances of the class (new objects). This version of the newInstance() method is more powerful than its counterpart in the Class class because this version is not confined to the use of constructors without arguments.

The Constructor class cannot be instantiated directly, but several methods of the Class class can be used to obtain a reference to a Constructor object.

As with Field and Method, a Constructor permits widening conversions to occur when matching the actual parameters to the newInstance() method with the formal parameters of the underlying constructor. An IllegalArgumentException will be thrown if a narrowing conversion would occur.

Other New Capabilities

In addition to the three new classes and the interface described above, the reflection API also provides: Generally speaking, the reflection methods deal only with objects. If you need to deal with primitive types, you will need to wrap them in a standard Java wrapper.

There are also some additions to the java.lang package that support reflection.

The Model for Reflection

As mentioned earlier, only the JVM can create instances of Field, Method, and Constructor because they are all final. Objects of these classes created by the JVM can be used to: The new Array class provides static methods for creating new arrays, and for getting and setting the elements of arrays.

The Modifier Class

It is not possible to instantiate objects of the class named Modifier. This class provides class methods to decode modifiers for classes and members (private, protected, etc.) that are returned by the methods of other classes.

Security

It is recommended that you review the information on the Security Model for the Core Reflection API to learn the details.

In general, according to the JDK 1.1 documentation:
 
"standard Java language access control checks -- for protected, default (package) access, and private classes and members -- will normally occur when the individual reflected members are used to operate on the underlying members of objects, that is, to get or set field values, to invoke methods, or to create and initialize new objects."
.

Data Conversion

Certain methods, such as methods for getting and setting field and array component values, perform automatic data conversions between values of primitive types and objects of class types.

There are two types of automatic data conversions.

In addition, field access and method invocation permit widening conversions on primitive and reference types as defined in The Java Language Specification, section 5.

The rules for conversions are:

The eight wrapper classes have names like java.lang.Boolean which correspond to the primitive types of the same name.

A method that is declared void returns the special reference null when it is invoked via Method.invoke.

The same widening conversions may be performed at run-time as are permitted in method invocation contexts at compile time. These conversions are defined in The Java Language Specification, section 5.3.

Conversions may be performed at runtime

Packaging

The Core Reflection API is in a new subpackage of java.lang named java.lang.reflect.

Sample Program

As mentioned earlier, we will begin our study of reflection by emphasizing the use of the Method class. Subsequent lessons will deal with the Field, Constructor, and Array classes. We will pick up the other classes, such as the Modifier class along the way.

The purpose of this program is to demonstrate the use of most of the methods of the Method class

A test class named TstCls is defined which contains two methods named setFlds() and showFlds(). These methods are not necessarily designed to make sense. Rather, they are designed to be good test cases for the methods of the Method class. For example, one of the methods throws some exceptions that make no operational sense at all.

A Class object is created to represent the TstCls class.

This Class object is used to create an array of Method objects where each Method objects represents one of the methods of the class.

A loop iterates on the array of Method objects, using each object in the array to obtain and display information about the method such as: name, declaring class, modifiers, return type, hash code, etc.

For each method, an array of Class objects is created where each object in the array represents one of the parameters to the method. A loop is used to iterate on this array displaying information about each parameter.

Also, for each method, an array of Class objects is created where each object in the array represents an exception that is thrown by the method. A loop is used to iterate on this array displaying information about each different type of exception that the method throws.

Then a blank line is displayed, and the program goes back to the top of the outer loop and displays information about the next method.

One important method that this program does not invoke on the objects of type Method is the invoke() method. The invoke() method can be used in conjunction with an object of type Method to invoke a method on a target object. The invoke() method will be the topic of one or more subsequent lessons..

This program was tested using JDK 1.1.3 under Win95.

The output from the program is shown later in the lesson.

Interesting Code Fragments

First we define a test class that can be investigated using the methods of the Method class. As mentioned before, this class isn't necessarily designed to make sense. Rather, it is designed to exercise the methods of the Method class.
 
class TstCls{
  String strFld;
  Date dteFld;
  
  public final void setFlds(String strFld, Date dteFld){
    this.strFld = strFld;
    this.dteFld = dteFld;
  }//end setFlds

  protected void showFlds() 
       throws InterruptedException,ClassNotFoundException{
    System.out.println(strFld);
  }//end showFlds
}//end TstCls
The entire remainder of the program is contained in the main() method of the controlling class. Because of its length, we will need to break it into sections and discuss the different sections individually.

Note also that because of some exceptions that can be thrown, almost all of the main() method is wrapped in a try block followed by a catch block. That is standard try/catch methodology and you can see how it is done in the complete program listing at the end of the lesson. Therefore, we won't discuss it further in this section of the lesson.

The next interesting code fragment is a list of local variables that are used within the program. I show them here just so that you can become familiar with their names.
 
    Class theClass;    //Various kinds of data are
    Method[] theMthds; // stored in these variables
    Class[] theParams;
    Class[] theExceps;
Our next interesting code fragment instantiates an object of the class Class that represents the class named TstCls. Notice that we did not directly instantiate an object of the Class class using the new operator. Rather, we used the forName() method of the Class class to obtain the Class object.

Following this, we use the Class object, along with the getDeclaredMethods() methods of the Class class to create an array of Method references and we assign that array to the reference variable declared earlier named theMethods.
 
      theClass = Class.forName("TstCls");
      
      theMthds = theClass.getDeclaredMethods();
Each element of the array named theMthds contains a reference to one Method object for every method in our class (so it should have two elements).

Note that there are subtle differences between similar sounding methods in the Class class (such as getDeclaredMethods and getMethods) and you must read the documentation very carefully to make certain that you know what you are getting. For example, getMethods() only provides a Method object for public methods.

It is difficult to break the code up at this point in a way that makes sense. What we have is nested loops. The outer loop iterates on the array of Method objects extracting various kinds of information. Some of the types of information that is extracted is single-valued, and some of it is multi-valued. Therefore, it is necessary for us to produce arrays inside the outer loop in some cases and to iterate on each of those arrays to properly display the information.

I am still going to present this code in chunks, and if you get lost, you can look in the complete program listing at the end of the lesson to see how it all fits together.

The next interesting code fragment includes the beginning of the outer loop and several statements that extract and display single-valued information about a specific method. Remember that this loop iterates once for each different method in the class under investigation.

For the most part, the name of the method being invoked indicates the type of information that is extracted and displayed about the method. I have highlighted the critical method names to make them easy for you to spot. You can compare this code with the output from the program that will be presented later.
 
      for(int i = 0; i < theMthds.length; i++){
        System.out.println("Method: " + theMthds[i]);
        System.out.println(
                        "Name: " + theMthds[i].getName());
        System.out.println(
                "Declaring Class: " 
                       + theMthds[i].getDeclaringClass());
        System.out.println(
                    "Modifiers: " + Modifier.toString(
                             theMthds[i].getModifiers()));
        System.out.println(
                        "Return Type: " + theMthds[i].
                                         getReturnType());
        System.out.println(
                   "hashCode: " + theMthds[i].hashCode());
There is one statement here having to do with modifiers that may not be intuitive. Modifiers are things like public, private, abstract, etc. As you are aware, they can exist in various combinations.

The method named getModifiers() returns an int where the existence of each possible modifier is encoded into a single bit in the int value. The Modifier class provides about a dozen methods that can be used to decode the int value to determine which set of modifiers it represents. All but one of those methods tests to see if the int value includes a bit for a specific single modifier. However, the overridden toString() method of the Modifier class produces a String that is a list of all the modifiers represented by the int value.

The next interesting code fragment gets into the realm of extracting multi-valued information about the method. This fragment has to do with the method parameters. Obviously each method can have none, one, or more parameters.

The getParameterTypes() method of the Method class returns one reference to a Class object for every parameter in the formal argument list for the method. It does that by returning an array of references of type Class. We assign the returned array to a previously-declared reference variable of the Class[] type.

Then we use a loop to iterate on this array to display the type of each of the parameters in the formal argument list. If the length of the array is zero, we display the string "Param: none".
 
        theParams = theMthds[i].getParameterTypes();
        System.out.println("Parameters");
        if(theParams.length != 0)
          for(int j = 0; j < theParams.length;j++)
            System.out.println(
                    "  Param: " + j + " " + theParams[j]);
        else System.out.println("  Param: none");
Just as each method can have multiple parameters, each method can also throw multiple exceptions. The method of the Method class named getExceptionTypes() also returns an array of Class references where each reference is to a  Class object that represents the exception. Again, we assign this array of references to a pre-defined reference variable of type Class[] and then iterate on that array to extract and display information about the exceptions thrown by the method.

If the length of the array is zero, we simply display the string "Excep: none".
 
        theExceps = theMthds[i].getExceptionTypes();
        System.out.println("Exceptions");
        if(theExceps.length != 0)
          for(int j = 0; j < theExceps.length;j++)
            System.out.println(
                "  Excep: " + j + " " + theExceps[j]);
        else System.out.println("  Excep: none");
Following this, we display a blank line and go back to the top of the loop to see if there are more methods to be examined. Then we throw in a couple of catch blocks that are needed to deal with exceptions that may have been thrown, and that is the end of the program.

The output produced by running this program is shown below. Manual line breaks were inserted in some cases to force the material to fit on the page.
 
Method: public final void 
           TstCls.setFlds(java.lang.String,java.util.Date)
Name: setFlds
Declaring Class: class TstCls
Modifiers: public final
Return Type: void
hashCode: 152758476
Parameters
  Param: 0 class java.lang.String
  Param: 1 class java.util.Date
Exceptions
  Excep: none

Method: protected void TstCls.showFlds() 
                    throws java.lang.InterruptedException,
                          java.lang.ClassNotFoundException
Name: showFlds
Declaring Class: class TstCls
Modifiers: protected
Return Type: void
hashCode: -1159789721
Parameters
  Param: none
Exceptions
  Excep: 0 class java.lang.InterruptedException
  Excep: 1 class java.lang.ClassNotFoundException
So there you have it, a major introduction to the reflection API. In the next lesson, we will start looking at how you might use the invoke() method of the Method class to do some pretty interesting things, including the design of more sophisticated adapters for dealing with events.

Program Listing

/*File Reflections03.java Copyright 1998, R.G.Baldwin

The purpose of this program is to demonstrate most of the
methods of the java.lang.reflect.Method class

This program was tested using JDK 1.1.3 under Win95.
**********************************************************/
import java.lang.reflect.*;
import java.util.*;
//=======================================================//

//Define a test class that can be investigated using the
// methods of the Method class.  This class isn't 
// necessarily designed to make sense.  Rather, it is
// designed to exercise the methods of the Method class.
class TstCls{
  String strFld;
  Date dteFld;
  //-----------------------------------------------------//
  
  public final void setFlds(String strFld, Date dteFld){
    this.strFld = strFld;
    this.dteFld = dteFld;
  }//end setFlds
  //-----------------------------------------------------//

  protected void showFlds() 
       throws InterruptedException,ClassNotFoundException{
    System.out.println(strFld);
  }//end showFlds
  //-----------------------------------------------------//
}//end TstCls
//=======================================================//

class Reflections03 {

  //-----------------------------------------------------//
  
  public static void main(String[] args) {
    Class theClass;    //Various kinds of data are
    Method[] theMthds; // stored in these variables
    Class[] theParams;
    Class[] theExceps;
    try{
      //Create a Class object for class named TstCls
      theClass = Class.forName("TstCls");
      
      //Create an array containing Method objects
      // describing all of the methods in the class
      // named TstCls
      theMthds = theClass.getDeclaredMethods();
      
      //Iterate on the array of Method objects, extracting
      // and displaying information about each of the
      // methods described by those objects.
      for(int i = 0; i < theMthds.length; i++){
        System.out.println("Method: " + theMthds[i]);
        System.out.println(
                        "Name: " + theMthds[i].getName());
        System.out.println(
                "Declaring Class: " 
                       + theMthds[i].getDeclaringClass());
        System.out.println(
                    "Modifiers: " + Modifier.toString(
                             theMthds[i].getModifiers()));
        System.out.println(
                        "Return Type: " + theMthds[i].
                                         getReturnType());
        System.out.println(
                   "hashCode: " + theMthds[i].hashCode());

        //Create an array of Class objects describing the
        // parameters of this method.  Iterate on the
        // array and describe each parameter.
        theParams = theMthds[i].getParameterTypes();
        System.out.println("Parameters");
        if(theParams.length != 0)
          for(int j = 0; j < theParams.length;j++)
            System.out.println(
                    "  Param: " + j + " " + theParams[j]);
        else System.out.println("  Param: none");

        //Create an array of Class objects describing the
        // exceptions thrown by this method.  Iterate on
        // the array and describe each exception.
        theExceps = theMthds[i].getExceptionTypes();
        System.out.println("Exceptions");
        if(theExceps.length != 0)
          for(int j = 0; j < theExceps.length;j++)
            System.out.println(
                "  Excep: " + j + " " + theExceps[j]);
        else System.out.println("  Excep: none");
        
        //Display a blank line and then describe the
        // next method in the array of methods.
        System.out.println();//blank line
      }//end for loop on i
    }//end try block
    catch(ClassNotFoundException e){}
    catch(SecurityException e){}
  }//end main()
}//end Reflections03
-end-