http://www.developer.com/

Back to article

Java Reflection in Action


May 11, 2005

A few basics

We are often faced with problems that could be solved simply and elegantly with reflection. Without it, our solutions are messy, cumbersome, and fragile. Consider the following scenarios:

  • Your project manager is committed to a pluggable framework, knowing that the system needs to accept new components even after it is built and deployed. You set up some interfaces and prepare a mechanism for patching your JAR, but you know that this will not completely satisfy the need for pluggability.
  • After months of developing a client-side application, marketing tells you that using a different remote mechanism will increase sales. Although switching is a good business decision, you now must reimplement all of your remote interfaces.
  • The public API to your module needs to accept calls only from specific packages to keep outsiders from misusing your module. You add a parameter to each of the API calls that will hold the package name of the calling class. But, now legitimate users must change their calls, and unwelcome code can fake a package name.

These scenarios illustrate, in turn, modularity, remote access, and security—and do not seem to have much in common. But they do: each one contains a change in requirements that can be satisfied only by making decisions and modifying code based upon the structure of the program.

Reimplementing interfaces, patching JAR files, and modifying method calls are all tedious and mechanical tasks. So mechanical, in fact, that you could write an algorithm that describes the necessary steps:

  1. Examine the program for its structure or data.
  2. Make decisions using the results of the examination.
  3. Change the behavior, structure, or data of the program based upon the decisions.

While these steps may be familiar to you in your role as programmer, they are not tasks that you would imagine a program doing. As a result, you assume that adapting code must be accomplished by a person sitting at a keyboard instead of by a program running on a computer. Learning reflection allows you to get beyond this assumption and make your program do this adaptation for you. Consider the following simple example:

public class HelloWorld {
   public void printName() {
      System.out.println(this.getClass().getName());
   }
}

The line

(new HelloWorld()).printName();

sends the string HelloWorld to standard out. Now let x be an instance of HelloWorld or one of its subclasses. The line

x.printName();

sends the string naming the class to standard out.

This small example is more dramatic than it seems—it contains each of the steps previously mentioned. The printName method examines the object for its class (this.getClass()). In doing so, the decision of what to print is made by delegating to the object's class. The method acts on this decision by printing the returned name. Without being overridden, the printName method behaves differently for each subclass than it does for HelloWorld. The printName method is flexible; it adapts to the class that inherits it, causing the change in behavior. As we build our examples in scope and complexity, we will show you many more ways to attain flexibility using reflection.

1.1 Reflection's value proposition

Reflection is the ability of a running program to examine itself and its software environment, and to change what it does depending on what it finds.

To perform this self-examination, a program needs to have a representation of itself. This information we call metadata. In an object-oriented world, metadata is organized into objects, called metaobjects. The runtime self-examination of the metaobjects is called introspection.

As we saw in the small example above, the introspection step is followed by behavior change. In general, there are three techniques that a reflection API can use to facilitate behavior change: direct metaobject modification, operations for using metadata (such as dynamic method invocation), and intercession, in which code is permitted to intercede in various phases of program execution. Java supplies a rich set of operations for using metadata and just a few important intercession capabilities. In addition, Java avoids many complications by not allowing direct metaobject modification.

These features give reflection the power to make your software flexible. Applications programmed with reflection adapt more easily to changing requirements. Reflective components are more likely to be reused flawlessly in other applications. These benefits are available in your current Java development kit.

Reflection is powerful, but it is not magical. You must master the subject in order to make your software flexible. It's not enough to just learn the concepts and the use of the API. You must also be able to distinguish between situations when Reflection is absolutely required from those when it may be used advantageously from those when it should be shunned. The examples in our book, Java Reflection in Action through Manning Publications, can help you acquire this skill. If you decide to read the book you will understand the three issues that have thus far impeded the broad use of reflection:

  • security
  • code complexity
  • runtime performance

You will learn that the concern over security was misguided. Java is so well crafted and its reflection API so carefully constrained that security is controlled simply. By learning when to use reflection and when not to, you will avoid unnecessarily complex code that can often be the result of amateurish use of reflection. In addition, you will learn to evaluate the performance of your designs, thereby ensuring the resulting code satisfies its performance requirements.

This introduction describes reflection, but scarcely reveals its value in this limited space. This article and the next will cover:

  • Reflection basics
  • Class fundamentals
  • Using methods reflectively

Software maintenance costs run three to four or more times development costs. The software marketplace is increasing its demand for flexibility. Knowing how to produce flexible code increases your value in the marketplace. Reflection—introspection followed by behavior change—is the path to flexible software. The promise of reflection is great and its time has come. Let's begin.

1.2 Enter George the programmer

George is a programmer at Wildlife Components, a leading animal simulation software company. In his daily work, George faces many challenges such as the ones previously mentioned. Throughout this book, we will follow George as he discovers the benefits of implementing reflective solutions.

For one project, George is working on a team that is implementing a user interface. George's team uses several standard Java visual components, others that are developed in house, a few that are open source, and still others that have been licensed from third parties. All of these components are integrated to form the user interface for the team's application.

Each of these components provides a setColor method that takes a java.awt.Color parameter. However, the hierarchies are set up such that the only common base class for all of them is java.lang.Object. These components cannot be referenced using a common type that supports this setColor method.

This situation presents a problem for George's team. They just want to call setColor regardless of a component's concrete type. The lack of a common type that declares setColor means more work for the team. In case this scenario seems contrived, we invite you to explore the JDK API and see the number of classes that support the same method but implement no common interface.

1.2.1 Choosing Reflection

Given a component, the team's code must accomplish two steps:

  1. Discover a setColor method supported by the component.
  2. Call that setColor method with the desired color.

There are many alternatives for accomplishing these steps manually. Let's examine the results of each of these.

If George's team controlled all of the source code, the components could be refactored to implement a common interface that declares setColor. Then, each component could be referenced by that interface type and setColor could be invoked without knowing the concrete type. However, the team does not control the standard Java components or third-party components. Even if they changed the open source components, the open source project might not accept the change, leaving the team with additional maintenance.

Alternatively, the team could implement an adapter for each component. Each such adapter could implement a common interface and delegate the setColor call to the concrete component. However, because of the large number of component classes that the team is using, the solution would cause an explosion in the number of classes to maintain. In addition, because of the large number of component instances, this solution would cause an explosion of the number of objects in the system at runtime. These trade-offs make implementing an adapter an undesirable option.

Using instanceof and casting to discover concrete types at runtime is another alternative, but it leaves several maintenance problems for George's team. First, the code would become bloated with conditionals and casts, making it difficult to read and understand. Second, the code would become coupled with each concrete type. This coupling would make it more difficult for the team to add, remove, or change components. These problems make instanceof and casting an unfavorable alternative.

Each of these alternatives involves program changes that adjust or discover the type of a component. George understands that it is only necessary to find a setColor method and call it. Having studied a little reflection, he understands how to query an object's class for a method at runtime. Once it is found, he knows that a method can also be invoked using reflection. Reflection is uniquely suited to solving this problem because it does not over-constrain the solution with type information.

1.2.2 Programming a reflective solution

To solve his team's problem, George writes the static utility method setObjectColor in listing 1. George's team can pass a visual component to this utility method along with a color. This method finds the setColor method supported by the object's class and calls it with the color as an argument.

Listing 1 George's setObjectColor code



Click here for a larger image.

This utility method satisfies the team's goal of being able to set a component's color without knowing its concrete type. The method accomplishes its goals without invading the source code of any of the components. It also avoids source code bloating, memory bloating, and unnecessary coupling. George has implemented an extremely flexible and effective solution.

Two lines in listing 1 use reflection to examine the structure of the parameter obj:

Editor's Note: The following numbers relate to the code callouts in Listing 1.1.
  1. This line of code queries the object for its class.
  2. This line queries the class for a setColor method that takes a Color argument.

In combination, these two lines accomplish the first task of finding a setColor method to call.

These queries are each a form of introspection, a term for reflective features that allow a program to examine itself. We say that setObjectColor introspects on its parameter, obj. There is a corresponding form of introspection for each feature of a class. We will examine each of these forms of introspection over the next few chapters.

One line in listing 1 actually affects the behavior of the program:

  1. This line calls the resulting method on obj, passing it the color—This reflective method call can also be referred to as dynamic invocation. Dynamic invocation is a feature that enables a program to call a method on an object at runtime without specifying which method at compile time.

In the example, George does not know which setColor method to call when writing the code because he does not know the type of the obj parameter. George's program discovers which setColor method is available at runtime through introspection. Dynamic invocation enables George's program to act upon the information gained through introspection and make the solution work. Other reflective mechanisms for affecting program behavior will be covered throughout the rest of the book.

Not every class supports a setColor method. With a static call to setColor, the compiler reports an error if the object's class does not support setColor. When using introspection, it is not known until runtime whether or not a setColor method is supported:

  1. The class of obj does not support a setColor method—It is important for introspective code to handle this exceptional case. George has been guaranteed by his team that each visual component supports setColor. If that method is not supported by the type of the obj parameter, his utility method has been passed an illegal argument. He handles this by having setObjectColor throw an IllegalArgumentException.

The setObjectColor utility method may not have access to nonpublic setColor methods. In addition, during the dynamic invocation, the setColor method may throw an exception:

  1. The class containing listing 1.1 does not have access privileges to call a protected, package, or private visibility setColor method.
  2. The invoked setColor method throws an exception.
Editor's Note: This ends the numbers relating to the code callouts in Listing 1.

It is important for methods using dynamic invocation to handle these cases properly. For simplicity's sake, the code in listing 1 handles these exceptions by wrapping them in runtime exceptions. For production code, of course, this would be wrapped in an exception that the team agrees on and declared in the utility method's throws clause.

All of this runtime processing also takes more time than casts and static invocation. The method calls for introspection are not necessary if the information is known at compile time. Dynamic invocation introduces latency by resolving which method to call and checking access at runtime rather than at compile time. Chapter 9 of my book discusses analysis techniques for balancing performance trade-offs with the tremendous flexibility benefits that reflection can give you.

The rest of this chapter focuses on the concepts necessary to fully understand listing 1. We examine, in detail, the classes that George uses to make it work. We also discuss the elements supported by Java that allow George such a flexible solution.

1.3 Examining running programs

Reflection is a program's ability to examine and change its behavior and structure at runtime. The scenarios previously mentioned have already implied that reflection gives programmers some pretty impressive benefits. Let's take a closer look at what reflective abilities mean for the structure of Java.

Think of introspection as looking at yourself in a mirror. The mirror provides you with a representation of yourself—your reflection—to examine. Examining yourself in a mirror gives you all sorts of useful information, such as what shirt goes with your brown pants or whether you have something green stuck in your teeth. That information can be invaluable in adjusting the structure of your wardrobe and hygiene.

A mirror can also tell you things about your behavior. You can examine whether a smile looks sincere or whether a gesture looks too exaggerated. This information can be critical to understanding how to adjust your behavior to make the right impression on other people.

Similarly, in order to introspect, a program must have access to a representation of itself. This self-representation is the most important structural element of a reflective system. By examining its self-representation, a program can obtain the right information about its structure and behavior to make important decisions.

Listing 1.1 uses instances of Class and Method to find the appropriate setColor method to invoke. These objects are part of Java's self-representation. We refer to objects that are part of a program's self-representation as metaobjects. Meta is a prefix that usually means about or beyond. In this case, metaobjects are objects that hold information about the program.

Class and Method are classes whose instances represent the program. We refer to these as classes of metaobjects or metaobject classes. Metaobject classes are most of what make up Java's reflection API.

We refer to objects that are used to accomplish the main purposes of an application as base-level objects. In the setObjectColor example above, the application that calls George's method as well as the objects passed to it as parameters are base-level objects. We refer to the nonreflective parts of a program as the base program.

Metaobjects represent parts of the running application, and, therefore, may describe the base program. Figure 1 shows the instanceof relationship between base-level objects and the objects that represent their classes. The diagramming convention used for figure 1.1 is the Unified Modeling Language (UML). For readers unfamiliar with UML, we will describe the conventions briefly in section 1.7. For the moment, it is important to understand that the figure can be read as "fido, a base-level object, is an instance of Dog, a class object on the metalevel."

Metaobjects are a convenient self-representation for reflective programming. Imagine the difficulty that George would have in accomplishing his task if he had tried to use the source code or the bytecodes as a representation. He would have to parse the program to even begin examining the class for its methods. Instead, Java metaobjects provide all of the information he needs without additional parsing.

Metaobjects often also provide ways of changing program structure, behavior, or data. In our example, George uses dynamic invocation to call a method that he finds through introspection. Other reflective abilities that make changes include reflective construction, dynamic loading, and intercepting method calls. This book shows how to use these mechanisms and others to solve common but difficult software problems.

Figure 1 Dog is a class object, a metaobject that represents the class Dog. The object fido is an instance of Dog operating within the application. The instanceof relationship, represented in this diagram by a dependency, connects objects on the base level to an object that represents their class on the metalevel.

1.4 Finding a method at runtime

At the beginning of our example, George's setObjectColor method is passed a parameter obj of type Object. The method cannot do any introspection until it knows the class of that parameter. Therefore, its first step is to query for the parameter's class:

Class cls = obj.getClass();

The getClass method is used to access an object's class at runtime. The getClass method is often used to begin reflective programming because many reflective tasks require objects representing classes. The getClass method is introduced by java.lang.Object, so any object in Java can be queried for its class1.

The getClass method returns an instance of java.lang.Class. Instances of Class are the metaobjects that Java uses to represent the classes that make up a program. Throughout this book, we use the term class object to mean an instance of java.lang.Class. Class objects are the most important kind of metaobject because all Java programs consist solely of classes.

Class objects provide programming metadata about a class's fields, methods, constructors, and nested classes. Class objects also provide information about the inheritance hierarchy and provide access to reflective facilities. For this chapter, we will concentrate on the use of Class in listing 1 and related fundamentals.

Once the setObjectColor method has discovered the class of its parameter, it queries that class for the method it wants to call:

Method method = cls.getMethod("setColor", new Class[] {Color.class});

The first parameter to this query is a String containing the desired method's name, in this case, setColor. The second parameter is an array of class objects that identify the types of the method's parameters. In this case, we want a method that accepts one parameter of type Color, so we pass getMethod an array of one element containing the class object for Color.

Notice that the assignment does not use getClass to provide the class object for Color. The getClass method is useful for obtaining the class for an object reference, but when we know only the name of the class, we need another way. Class literals are Java's way to specify a class object statically. Syntactically, any class name followed by .class evaluates to a class object. In the example, George knows that setObjectColor always wants a method that takes one Color argument. He specifies this using Color.class.

Class has other methods for introspecting about methods. The signatures and return types for these methods are shown in table 1.1. As in the previous example, the queries use an array of Class to indicate the types of the parameters. In querying for a parameterless method, it is legal to supply null, which is treated the same as a zero-length array.

Table 1.1 The methods defined by Class for method query

Method Description
Method getMethod ( String name,
                   Class[] parameterTypes )
Returns a Method object that represents a public method (either declared or inherited) of the target Class object with the signature specified by the second parameters
Method[] getMethods ()
Returns an array of Method objects that represent all of the public methods (either declared or inherited) supported by the target Class object
Method getDeclaredMethod (
                 String name,
                 Class[] parameterTypes )
Returns a Method object that represents a declared method of the target Class object with the signature specified by the second parameters
Method[] getDeclaredMethods ()
Returns an array of Method objects that represent all of the methods declared by the target Classobject

As their names indicate, getDeclaredMethod and getDeclaredMethods return method objects for methods explicitly declared by a class. The set of declared methods does not include methods that the class inherits. However, these two queries do return methods of all visibilities—public, protected, package, and private.

The queries getMethod and getMethods return method objects for a class's public methods. The set of methods covered by these two includes both methods declared by the class and those it inherits from superclasses. However, these queries return only a class's public methods.

A programmer querying a class using getDeclaredMethod might accidentally specify a method that the class does not declare. In this case, the query fails with a NoSuchMethodException. The same exception is thrown when getMethod fails to find a method among a class's public methods.

In the example, George needs to find a method, and he does so using one of the methods from table 1.1. Once retrieved, these method objects are used to access information about methods and even call them. We discuss method objects in detail later in this chapter, but first let's take a closer look at how class objects are used with the methods from table 1.1.

End Notes

1 The getClass method is final. This keeps Java programmers from fooling reflective programs. If it were not final, a programmer could override getClass to return the wrong class.

More to Come

The rest of this material will appear on our website starting May 25th.

About the Authors

Dr. Ira Forman is a senior software engineer at IBM. He started working on reflection in the early 1990s when he developed IBM's SOM Metaclass Framework. Nate Forman works for Ticom Geomatics where he uses reflection in day-to-day problems. Ira and Nate are father and son. They live in Austin, Texas.

About the Book

Java Reflection in Action By Ira R. Forman and Nate Forman


Published October 2004, Softbound, 300 pages
Published by Manning Publications Co.
ISBN 1932394184
Retail price: $44.95
Ebook price: $22.50. To purchase the ebook go to http://www.manning.com/forman.
This material is from Chapter 1 of the book.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date