Reflection: run-time class information | CodeGuru

Reflection: run-time class information

Bruce Eckel’s Thinking in Java Contents | Prev | Next class information If you don’t know the precise type of an object, RTTI will tell you. However, there’s a limitation: the type must be known at compile time in order for you to be able to detect it using RTTI and do something useful with […]

Written By
CodeGuru Staff
CodeGuru Staff
Mar 1, 2001
6 minute read
CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More

class
information

If


you don’t know the precise type of an object, RTTI will tell you.


However, there’s a limitation: the type must be known at compile time in


order for you to be able to detect it using RTTI and do something useful with


the information. Put another way, the compiler must know about all the classes


you’re working with for RTTI.

This


doesn’t seem like that much of a limitation at first, but suppose


you’re given a handle to an object that’s not in your program


space. In fact, the class of the object isn’t even available to your


program at compile time. For example, suppose you get a bunch of bytes from a


disk file or from a network connection and you’re told that those bytes


represent a class. Since the compiler can’t know about the class while


it’s compiling the code, how can you possibly use such a class?

In


a traditional programming environment this seems like a far-fetched scenario.


But as we move into a larger programming world there are important cases in


which this happens. The first is component-based programming in which you build


projects using

Rapid
Application Development

(RAD) in an application builder tool. This is a visual approach to creating a
program (which you see on the screen as a
form)
by moving icons that represent components onto the form. These components are
then configured by setting some of their values at program time. This
design-time configuration requires that any component be instantiable and that
it expose some part of itself and allow its values to be read and set. In
addition, components that handle GUI events must expose information about
appropriate methods so that the RAD environment
can
assist the programmer in overriding these event-handling methods. Reflection
provides the mechanism to detect the available methods and produce the method
names.
Java
1.1 provides a structure for component-based programming through Java Beans
(described in Chapter 13).

Another


compelling motivation for discovering class information at run-time is to


provide the ability to create and execute objects on remote platforms across a


network. This is called


Remote
Method Invocation

(RMI) and it allows a Java program (version 1.1 and higher) to have objects


distributed across many machines. This distribution can happen for a number of


reasons: perhaps you’re doing a computation-intensive task and you want


to break it up and put pieces on machines that are idle in order to speed


things up. In some situations you might want to place code that handles


particular types of tasks (e.g. “Business Rules” in a multi-tier


client/server architecture) on a particular machine so that machine becomes a


common repository describing those actions and it can be easily changed to


affect everyone in the system. (This is an interesting development since the


machine exists solely to make software changes easy!) Along these lines,


distributed computing also supports specialized hardware that might be good at


a particular task – matrix inversions, for example – but


inappropriate or too expensive for general purpose programming.

In


Java 1.1

,
the class
Class
(described previously in this chapter) is extended to support the concept of
reflection,
and there’s an additional library,
java.lang.reflect,
with
classes
Field,
Method,
and
Constructor
(each
of which implement the
Member
interface
).
Objects of these types are created by the JVM at run-time to represent the
corresponding member in the unknown class. You can then use the
Constructors
to create new objects, the
get( )
and
set( )
methods to read and modify the fields associated with
Field
objects, and the
invoke( )
method to call a method associated with a
Method
object. In addition, you can call the convenience methods
getFields( ),
getMethods( ),
getConstructors( ),
etc., to return arrays of the objects representing the fields, methods, and
constructors. (You can find out more by looking up the class
Class
in your online documentation.)

Thus,
the class information for anonymous objects can be completely determined at run
time, and nothing need be known at compile time.

It’s


important to realize that there’s nothing magic about reflection. When


you’re using reflection to interact with an object of an unknown type,


the JVM will simply look at the object and see that it belongs to a particular


class (just like ordinary RTTI) but then, before it can do anything else, the


Class

object must be loaded. Thus, the


.class

file for that particular type must still be available to the JVM, either on the


local machine or across the network. So the true

difference
between RTTI and reflection is that with RTTI, the compiler opens and examines
the
.class
file at compile time. Put another way, you can call all the methods of an
object in the “normal” way. With reflection, the
.class
file is unavailable at compile time; it is opened and examined by the run-time
environment.

A
class method extractor

You’ll


rarely need to use the reflection tools directly; they’re in the language


to support the other Java features such as object serialization (described in


Chapter 10), Java Beans, and RMI (described later in the book). However, there


are times when it’s quite useful to be able to dynamically extract


information about a class. One extremely useful tool is a class method


extractor. As mentioned before, looking at a class definition source code or


online documentation shows only the methods that are defined or overridden


within
that class definition

.


But there could be dozens more available to you that have come from base


classes. To locate these is both tedious and time consuming. Fortunately,


reflection provides a way to write a simple tool that will automatically show


you the entire interface. Here’s the way it works:

//: ShowMethods.java
// Using Java 1.1 reflection to show all the 
// methods of a class, even if the methods are 
// defined in the base class.
import java.lang.reflect.*;
 
public class ShowMethods {
  static final String usage =
    "usage: n" +
    "ShowMethods qualified.class.namen" +
    "To show all methods in class or: n" +
    "ShowMethods qualified.class.name wordn" +
    "To search for methods involving 'word'";
  public static void main(String[] args) {
    if(args.length < 1) {
      System.out.println(usage);
      System.exit(0);
    }
    try {
      Class c = Class.forName(args[0]);
      Method[] m = c.getMethods();
      Constructor[] ctor = c.getConstructors();
      if(args.length == 1) {
        for (int i = 0; i < m.length; i++)
          System.out.println(m[i].toString());
        for (int i = 0; i < ctor.length; i++)
          System.out.println(ctor[i].toString());
      }
      else {
        for (int i = 0; i < m.length; i++)
          if(m[i].toString()
             .indexOf(args[1])!= -1)
            System.out.println(m[i].toString());
        for (int i = 0; i < ctor.length; i++)
          if(ctor[i].toString()
             .indexOf(args[1])!= -1)
          System.out.println(ctor[i].toString());
      }
    } catch (ClassNotFoundException e) {
      System.out.println("No such class: " + e);
    }
  }
} ///:~ 

The


Class

methods

getMethods( )
and
getConstructors( )
return an array of
Method
and
Constructor,
respectively. Each of these classes has further methods to dissect the names,
arguments, and return values of the methods they represent. But you can also
just use
toString( ),
as is done here, to produce a
String
with the entire method signature. The rest of the code is just for extracting
command line information, determining if a particular signature matches with
your target string (using
indexOf( )),
and printing the results.

This


shows reflection in action, since the result produced by


Class.forName( )

cannot be known at compile-time, and therefore all the method signature


information is being extracted at run-time. If you investigate your online


documentation on reflection, you’ll see that there is enough support to


actually set up and make a method call on an object that’s totally


unknown at compile-time. Again, this is something you’ll probably never


need to do yourself – the support is there for Java and so a programming


environment can manipulate Java Beans – but it’s interesting.

An


interesting experiment is to run


java
ShowMethods ShowMethods

.


This produces a listing that includes a


public

default constructor, even though you can see from the code that no constructor


was defined. The constructor you see is the one that’s automatically


synthesized by the compiler. If you then make


ShowMethods

a non-


public

class (that is, friendly), the synthesized default constructor no longer shows


up in the output. The synthesized default constructor is automatically given


the same access as the class.

The


output for


ShowMethods

is still a little tedious. For example, here’s a portion of the output


produced by invoking


java
ShowMethods java.lang.String

:

public boolean
  java.lang.String.startsWith(java.lang.String,int)
public boolean
  java.lang.String.startsWith(java.lang.String)
public boolean <p><tt>  java.lang.String.endsWith(java.lang.String) </tt></p>

It


would be even nicer if the qualifiers like


java.lang

could be stripped off. The

StreamTokenizer
class introduced in the previous chapter can help solve this problem:
//: ShowMethodsClean.java
// ShowMethods with the qualifiers stripped
// to make the results easier to read
import java.lang.reflect.*;
import java.io.*;
 
public class ShowMethodsClean {
  static final String usage =
    "usage: n" +
    "ShowMethodsClean qualified.class.namen" +
    "To show all methods in class or: n" +
    "ShowMethodsClean qualif.class.name wordn" +
    "To search for methods involving 'word'";
  public static void main(String[] args) {
    if(args.length &lt; 1) {
      System.out.println(usage);
      System.exit(0);
    }
    try {
      Class c = Class.forName(args[0]);
      Method[] m = c.getMethods();
      Constructor[] ctor = c.getConstructors();
      // Convert to an array of cleaned Strings:
      String[] n =
        new String[m.length + ctor.length];
      for(int i = 0; i &lt; m.length; i++) {
        String s = m[i].toString();
        n[i] = StripQualifiers.strip(s);
      }
      for(int i = 0; i &lt; ctor.length; i++) {
        String s = ctor[i].toString();
        n[i + m.length] =
          StripQualifiers.strip(s);
      }
      if(args.length == 1)
        for (int i = 0; i &lt; n.length; i++)
          System.out.println(n[i]);
      else
        for (int i = 0; i &lt; n.length; i++)
          if(n[i].indexOf(args[1])!= -1)
            System.out.println(n[i]);
    } catch (ClassNotFoundException e) {
      System.out.println("No such class: " + e);
    }
  }
}
 
class StripQualifiers {
  private StreamTokenizer st;
  public StripQualifiers(String qualified) {
      st = new StreamTokenizer(
        new StringReader(qualified));
      st.ordinaryChar(' '); // Keep the spaces
  }
  public String getNext() {
    String s = null;
    try {
      if(st.nextToken() !=
            StreamTokenizer.TT_EOF) {
        switch(st.ttype) {
          case StreamTokenizer.TT_EOL:
            s = null;
            break;
          case StreamTokenizer.TT_NUMBER:
            s = Double.toString(st.nval);
            break;
          case StreamTokenizer.TT_WORD:
            s = new String(st.sval);
            break;
          default: // single character in ttype
            s = String.valueOf((char)st.ttype);
        }
      }
    } catch(IOException e) {
      System.out.println(e);
    }
    return s;
  }
  public static String strip(String qualified) {
    StripQualifiers sq =
      new StripQualifiers(qualified);
    String s = "", si;
    while((si = sq.getNext()) != null) {
      int lastDot = si.lastIndexOf('.');
      if(lastDot != -1)
        si = si.substring(lastDot + 1);
      s += si;
    }
    return s;
  }
} ///:~ 

The


class


ShowMethodsClean

is quite similar to the previous


ShowMethods

,


except that it takes the arrays of


Method

and


Constructor

and converts them into a single array of


String

.


Each of these


String

objects is then passed through



StripQualifiers.Strip( )

to remove all the method qualification. As you can see, this uses the


StreamTokenizer

and


String

manipulation to do its work.

This


tool can be a real time-saver while you’re programming, when you


can’t remember if a class has a particular method and you don’t


want to go walking through the class hierarchy in the online documentation, or


if you don’t know whether that class can do anything with, for example,


Color

objects.

Chapter


17 contains a GUI version of this program so you can leave it running while


you’re writing code, to allow quick lookups.


Contents

|

Prev

|

Next
CodeGuru Logo

CodeGuru covers topics related to Microsoft-related software development, mobile development, database management, and web application programming. In addition to tutorials and how-tos that teach programmers how to code in Microsoft-related languages and frameworks like C# and .Net, we also publish articles on software development tools, the latest in developer news, and advice for project managers. Cloud services such as Microsoft Azure and database options including SQL Server and MSSQL are also frequently covered.

Property of TechnologyAdvice. © 2026 TechnologyAdvice. All Rights Reserved

Advertiser Disclosure: Some of the products that appear on this site are from companies from which TechnologyAdvice receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. TechnologyAdvice does not include all companies or all types of products available in the marketplace.