Recipe 25.2 Finding and Using Methods and Fields


Problem

You need more to find arbitrary method or field names in arbitrary classes.

Solution

Use the reflection package java.lang.reflect .

Discussion

If you just wanted to find fields and methods in one particular class, you wouldn't need this recipe; you could simply create an instance of the class using new and refer to its fields and methods directly. But this allows you to find methods and fields in any class, even classes that have not yet been written! Given a class object created as in Recipe 25.1, you can obtain a list of constructors, a list of methods, or a list of fields. The method getMethods( ) lists the methods available for a given class as an array of Method objects. Similarly, getFields( ) returns a list of Field objects. Since constructor methods are treated specially by Java, there is also a getConstructors( ) method, which returns an array of Constructor objects. Even though "class" is in the package java.lang, the Constructor, Method, and Field objects it returns are in java.lang.reflect, so you need an import of this package. The ListMethods class (Example 25-1) shows how get a list of methods in a class whose name is known at runtime.

Example 25-1. ListMethods.java
import java.lang.reflect.*; /**  * List the Constructors and methods  */ public class ListMethods {     public static void main(String[] argv) throws ClassNotFoundException {         if (argv.length == 0) {             System.err.println("Usage: ListMethods className");             return;         }         Class c = Class.forName(argv[0]);         Constructor[] cons = c.getConstructors( );         printList("Constructors", cons);         Method[] meths = c.getMethods( );         printList("Methods", meths);     }     static void printList(String s, Object[] o) {         System.out.println("*** " + s + " ***");         for (int i=0; i<o.length; i++)             System.out.println(o[i].toString( ));     } }

For example, you could run Example 25-1 on a class like java.lang.String and get a fairly lengthy list of methods; I'll only show part of the output so you can see what it looks like:

> java ListMethods java.lang.String *** Constructors *** public java.lang.String( ) public java.lang.String(java.lang.String) public java.lang.String(java.lang.StringBuffer) public java.lang.String(byte[]) // and many more... *** Methods *** public static java.lang.String java.lang.String.copyValueOf(char[]) public static java.lang.String java.lang.String.copyValueOf(char[],int,int) public static java.lang.String java.lang.String.valueOf(char) // and more valueOf( ) forms... public boolean java.lang.String.equals(java.lang.Object) public final native java.lang.Class java.lang.Object.getClass( ) // and more java.lang.Object methods... public char java.lang.String.charAt(int) public int java.lang.String.compareTo(java.lang.Object) public int java.lang.String.compareTo(java.lang.String)

You can see that this could be extended (almost literally) to write a BeanMethods class that would list only the set/get methods defined in a JavaBean (see Recipe 23.8).

Alternatively, you can find a particular method and invoke it, or find a particular field and refer to its value. Let's start by finding a given field, since that's the easiest. Example 25-2 is code that, given an Object and the name of a field, finds the field (gets a Field object) and then retrieves and prints the value of that Field as an int.

Example 25-2. FindField.java
import java.lang.reflect.*; import java.util.*; /** This class shows using Reflection to get a field from another class. */ public class FindField {     public static void main(String[] unused)      throws NoSuchFieldException, IllegalAccessException {         // Create instance of FindField         FindField gf = new FindField( );         // Create instance of target class (YearHolder defined below).         Object o = new YearHolder( );         // Use gf to extract a field from o.         System.out.println("The value of 'currentYear' is: " +             gf.intFieldValue(o, "currentYear"));     }     int intFieldValue(Object o, String name)     throws NoSuchFieldException, IllegalAccessException {         Class c = o.getClass( );         Field fld = c.getField(name);         int value = fld.getInt(o);         return value;     } } /** This is just a class that we want to get a field from */ class YearHolder {     /** Just a field that is used to show getting a field's value. */     public int currentYear = Calendar.getInstance( ).get(Calendar.YEAR); }

What if we need to find a method? The simplest way is to use the methods getMethod( ) and invoke( ). But this is not altogether trivial. Suppose that somebody gives us a reference to an object. We don't know its class but have been told that it should have this method:

public void work(String s) { }

We wish to invoke work( ). To find the method, we must make an array of Class objects, one per item in the calling list. So, in this case, we make an array containing only a reference to the class object for String. Since we know the name of the class at compile time, we'll use the shorter invocation String.class instead of Class.forName( ). This, plus the name of the method as a string, gets us entry into the getMethod( ) method of the Class object. If this succeeds, we have a Method object. But guess what? In order to invoke the method, we have to construct yet another array, this time an array of Object references actually containing the data to be passed to the invocation. We also, of course, need an instance of the class in whose context the method is to be run. For this demonstration class, we need to pass only a single string, as our array consists only of the string. Example 25-3 is the code that finds the method and invokes it.

Example 25-3. GetMethod.java
import java.lang.reflect.*; /** This class is just here to give us something to work on,  * with a println( ) call that will prove we got here. */ class X {     public void work(String s) {         System.out.println("Working on \"" + s + "\"");     } } /**  * Get a given method, and invoke it.  */ public class GetMethod {     public static void main(String[] argv) {         try {             Class clX = X.class; // or Class.forName("X");             // To find a method we need the array of matching Class types.             Class[] argTypes = {                 String.class             };             // Now find a Method object for the given method.             Method worker = clX.getMethod("work", argTypes);             // To INVOKE the method, we need its actual arguments, as an array.             Object[] theData = {                 "Chocolate Chips"             };             // The obvious last step: invoke the method.             worker.invoke(new X( ), theData);         } catch (Exception e) {             System.err.println("Invoke( ) failed: " + e);         }     } }

Not tiny, but it's still not bad. In most programming languages, you couldn't do that in the 40 lines it took us here.

A word of caution: when the arguments to a method are of a primitive type, such as int, you do not pass Integer.class into getMethod( ). Instead, you must use the class object representing the primitive type int. The easiest way to find this class is in the Integer class, as a public constant named TYPE, so you'd pass Integer.TYPE. The same is true for all the primitive types; for each, the corresponding wrapper class has the primitive class referred to as TYPE.



Java Cookbook
Java Cookbook, Second Edition
ISBN: 0596007019
EAN: 2147483647
Year: 2003
Pages: 409
Authors: Ian F Darwin

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net