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; you could simply create an instance of the class using new and refer to its fields and methods directly. 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 Section 25.2, 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. 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 to do this.

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 Section 23.8).

Alternately, 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), 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( ) to do the deed. 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 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 .

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.101.81