Skip to main content

skip to main content

developerWorks  >  Java technology  >

Reflection: A new way to discover information about Java classes

developerWorks
Document options

Document options requiring JavaScript are not displayed


Rate this page

Help us improve this content


Level: Introductory

Dan Becker (beckerdo@us.ibm.com)IBM

01 May 1998

Most people think of reflection as the return of an image in a mirror or as serious thought. Developers using the Java programming language can include another meaning: a way to discover information about Java classes.

Editor's note: The Reflection API has been enhanced since the publication of this article; readers can consult the JDK documentation for java.lang.Class and java.lang.reflect.* for more information.

Java Version 1.1 or later allows a Java program to discover information about the fields, methods, and constructors of loaded classes. Also, a Java program can instantiate a class and operate on the underlying objects, within security restrictions.

The Reflection Application Programming Interface (API) accommodates applications that work solely on public members of an object (based on its run-time class) or all the members declared by a given class. Although some of this capability is allowed in Java 1.02, the important methods, such as finding constructors or number and type of parameters, are available only in Java 1.1 or later.

In this article, I explore the uses of reflection and develop an example program that has many uses. The program can debug other programs, act as a developer's quick reference tool, or explore the class hierarchy of an object-oriented design.

Outlining the Class Hierarchy Using Java 1.02

At the simplest level, Java allows programs to inspect class information through the Java class named Class, located in the package java.lang. The most important methods are getName() and getSuperclass(), which allow you to print the name of the class and find the super class (or parent) of the given class.

Let's begin by expanding on an example given by the grandfathers of the Java Language, Ken Arnold and James Gosling, in the book The Java Programming Language . The Java application shown below creates an instance of itself and calls the printType(...) method for each command line argument.

In this example, the printType method encapsulates all the reflection code. It prints the name of the class, via the getName() method, and recursively calls itself on its super class, via the getSuperclass() method. This example is the simplest form of reflection and was supported in Java 1.01 and 1.02. Notice that we don't have to import the reflection package because the Java class Class and its methods are located in the java.lang package.

public class Inspector {
   // Methods
   public static void main( String[] args ) {
      Inspector inspector = new Inspector();
      
      try {
         for (int i = 0; i < args.length; i++ ) 
            inspector.printType( Class.forName( args[ i ] ), 0 );
      } catch ( ClassNotFoundException e ) {
         System.err.print( e );
      } /* endcatch */
   }

   public void printType( Class type, int depth ) {
      // Print class name
      System.out.println( "Class name " + depth + " is " + type.getName() );

      // Recurse super classes
      if ( type.getSuperclass() != null ) 
      // true for all except java.lang.Object
         printType( type.getSuperclass(), depth + 1 );
   }
}

When you compile and run this program, you should see output similar to the output shown below.

 [c:\]java Inspector java.lang.Integer
   Class name 0 is java.lang.Integer
   Class name 1 is java.lang.Number
   Class name 2 is java.lang.Object
   [c:\]_

The name of the given class is followed by all of its parent classes, ending with the mother of all Java classes, java.lang.Object. Note that the output reflects the fully qualified class name of java.lang.Integer since all Java class names are stored with their scope. There is no notion of package importing in the Java run time.

If you do not get similar results, check your code and your Java environment. Make any appropriate changes to product-similar results, or the rest of the examples in this article will also fail.



Back to top


Querying Interfaces Using Java 1.02

The first improvement that you can make to the program is to list the interfaces a class implements as well as the classes it extends. This is done with the Class getInterfaces() method, which is available in Java 1.02 or later. The "Print Interfaces" loop added to the printTypes(...) method below recursively prints the types that a class implements.

public void printType( Class type, int depth ) {
    // Calculate indent and proper class label
    StringBuffer indent = new StringBuffer();
    for (int i = 0; i < depth; i++ ) indent.append( "   " );
    String[] labels = ( depth == 0 ? basic : extended );
    System.out.print( indent + labels[ type.isInterface() ? 1 : 0 ] + " " );

    // Print class name
    System.out.println( type.getName() );
    // Recurse super classes
    if ( type.getSuperclass() != null ) // true for all except java.lang.Object
       printType( type.getSuperclass(), depth + 1 );

    // Print interfaces this class implements.
    Class[] interfaces = type.getInterfaces();
    for (int i = 0; i < interfaces.length; i++ ) 
       printType( interfaces[ i ], depth + 1 );
 }

 private final static String[]
    basic     = { "class", "interface" },
    extended  = { "extends", "implements" };

This example also contains "pretty printing" code that indents the output and prints a label "class" or "interface" to distinguish the two. Notice that within the first print statement I have added the isInterface() method to perform this check. At this point, all methods used in the program are available in Java 1.02.



Back to top


An Improved Java 1.02 Reflection Program

The results from running this code example are greatly improved from the first example. Not only do you get a complete class hierarchy, but you also see the interfaces that a class implements. One clue that this program was run under Java 1.1 or later is that the java.lang.Number class implements java.io.Serializable, a new interface introduced in the JDK 1.1.

[c:\]java Inspector java.lang.Integer
class java.lang.Integer
   extends java.lang.Number
      extends java.lang.Object
      implements java.io.Serializable
[c:\]_

These are simple examples of reflection. Now let's add more advanced introspection techniques to the printType() method. To accomplish this, you need the new features of Java 1.1.



Back to top


Using the Reflection Features of Java 1.1: Modifiers

The first bit of information you can obtain from the additional reflection features is the scope and visibility of a given class. To do this, you have to import the new java.lang.reflect package with an import statement at the beginning of the program. You also use the new getModifiers() method of the Class class. This adds one line to the middle of the printType method, right before you print the class name. You are using a static method from the new Modifier class to convert the modifiers of a class to a String that can be printed by a program.

import java.lang.reflect.*;

   public void printType( Class type, int depth ) {
      ...

      // Print class modifiers
      System.out.print( Modifier.toString( type.getModifiers() ) + " " );

      // Print class name
      System.out.println( type.getName() );

      ...
   }

This modification results in the output shown below. Notice the classes and interfaces now list their visibility (public) and other modifiers such as synchronized, final, or abstract.

[c:\]java Inspector java.lang.Integer
class public final synchronized java.lang.Integer
    extends public abstract synchronized java.lang.Number
        extends public synchronized java.lang.Object
        implements public interface java.io.Serializable
[c:\]_



Back to top


Using the Reflection Features of Java 1.1: Fields

You can now make a great leap in the amount and quality of information that is available from a Java class. You can query a class's constructors, methods, and fields (another name for object or instance data), using classes in the java.lang.reflect packag package. The classes Constructor, Method, and Field all implement the Member interface, and the methods for enumerating and printing these classes are very similar. The following examples concentrate on the Fields class. A complete example supporting Constructors and Methods is available on the IBM Developer Connection Web site and on the set of CDs.

The next example adds a printFields() method to the printType(...) method. The parameters for this method are a String for pretty printing and a class name that you would like to query.The definition of the printFields() method is shown below.

   public void printType( Class type, int depth ) {
      ...

      // Print class name
      System.out.println( type.getName() );

      // Print class fields
      printFields( indent.toString(), type );

      ...
   }

   void printFields( String indent, Class type ) {
      Field[] myFields = type.getDeclaredFields();

      for( int i = 0; i

The first step is to query the fields of the class using the getDeclaredFields() method of the Class class. This method returns an array of all the fields of a class. Then a loop goes through each Field in the array and prints its modifiers, type, and name using the getModifiers() , getType() , and getName() methods.

The getType() method returns a class. The name of this class is not printed directly; rather, the class is passed to the printArrayorClassType(...) method. This is an important step because the field may be an array and not merely a primitive or simple type.

If you use getName() to print the name of an array field, you get a garbled name. So it is important to find the type a class represents using the isArray() and getComponentType() methods of the class Class as shown in the following example.

   void printArrayorClassType( Class type ) {
      if ( type.isArray() )
         System.out.print( type.getComponentType().getName() + " [] " );
      else
         System.out.print( type.getName() + " " );
   }

Notice that I queried the fields of the class using a getDeclaredFields() method. This returns all the fields declared in a class: public, protected, and private. Typically, programs such as debuggers and heavy-duty developer-class inspectors use this type of method to report on all declared fields. There is also a method called getFields() that returns all public fields of a class, including any field inherited from a class's super type. Typically, applications such as Java beans and run-time object inspectors use this method to report on the public signature of an object. The example program in this article reports on all class information using the former type of reflection.

When you compile and run this program on a class such as java.lang.Integer, you can see five declared fields of the Integer class as well as one declared field of the Number class.

[c:\]java simp java.lang.Integer
class public final synchronized java.lang.Integer
      public static final int MIN_VALUE
      public static final int MAX_VALUE
      public static final java.lang.Class TYPE
      private int value
      private static final long serialVersionUID
   extends public abstract synchronized java.lang.Number



Back to top


Security Issues

As there are security concerns raised when running a Java applet versus running a Java application, there are also security concerns when using Java Reflection. Java Reflection uses two levels of checks to enforce security when using this API:

  1. The Java class Class is the only source for instantiating the Field, Method, and Constructor classes. Class delegates security checking to the system security manager, which contains the checkMemberAccess(Class,int) method on a class-by-class basis. This methods throws a SecurityException if access is not allowed.
  2. After satisfying the checks in step one, any code may query the reflected member for its identifying information. However, standard Java language checks are enforced when 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 is no notion of privileged code and no means to override the standard language access control checks.

So, in the class inspector example, you are allowed to query the constructors, methods, and fields of any class. But you might run into IllegalAccessExceptions if you attempt to do one of the following:

  • Use the Field class to get or set a field value
  • Use the Method class to invoke a method
  • Use the Constructor class to create and initialize a new instance of a class

These are points to remember if you wish to go beyond inspecting classes to creating and manipulating classes.



Back to top


Conclusion

If you've followed this article closely, you should now understand the workings of the entire Java reflection programming interface. It is now entirely possible to inspect and act on Java classes on the fly. This is important for creating programs that are extensible at run time. For example, given an array of pixels and a class name (let's say it's an image processing filter), one could write a program to search for the class by name, load it, look for the right method, and run the class method on the array of pixels. Users could add new image processing filters simply by providing a new class file. There is no need to buy a new program to support updates; simply add them yourself. Now that is a powerful and flexible program!



About the author

Dan Becker works in the IBM Software Division in Austin, Texas. He is responsible for the audio subsystem on various IBM implementations of the Java Sound API. Before that, Dan worked on porting previous Java Virtual Machines, the multimedia plugins for Netscape Navigator for OS/2, OpenDoc, and the Multimedia parts for OS/2 Warp, Version 4.0. Dan can be reached at beckerdo@us.ibm.com or at his public Web page at www.io.com/~beckerdo.




Rate this page


Please take a moment to complete this form to help us better serve you.



 


 


Not
useful
Extremely
useful
 


Share this....

digg Digg this story del.icio.us del.icio.us Slashdot Slashdot it!



Back to top