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." |
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.
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 |
interface java.lang.reflect.MemberFields
Methods |
class java.lang.reflect.Field
Methods |
class java.lang.reflect.Method
Methods |
class java.lang.reflect.Constructor
Methods |
class java.lang.reflect.Array
Methods |
class java.lang.reflect.Modifier
FieldsMethods |
class java.lang.reflect.InvocationTargetException
ConstructorsMethods |
Depending on security policy, this API can be used to:
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.
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.
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.
There are also some additions to the java.lang package that support reflection.
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." |
There are two types of automatic data conversions.
The rules for conversions are:
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
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.
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 |
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; |
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(); |
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()); |
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"); |
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"); |
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 |
/*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 |