What Is Reflection?
Reflection is the ability of objects, classes, and interfaces to tell you about themselves at runtime. The name "reflection" comes from the idea that if something can see its reflection, it can tell you about its appearance--or, in object-oriented terms, its external interface. Objects can tell you what class instantiated them. Classes can tell you what classes they inherit from, what interfaces they implement, what methods and fields they have (including constructors), and what their names are. They can also tell you other class attributes, such as whether it is public, abstract, or final. In fact, for methods, you can even get the return type and argument types; likewise, you can also get field types. Interfaces are essentially treated as classes and can tell you everything about themselves that a class can, including whether the class in question is truly a class or actually an interface. What's even better than being able to query objects, classes, and interfaces at runtime is that the Java language designers took things one step further to allow you to manipulate things at runtime. For example, you can create an object whose name you don't know until runtime. You can then query this object for its class type, query its class for the methods that it implements, and then invoke one of these methods. You do all of this without knowing anything about the original object.
A Simple Example
The following code creates an object called myMirror
of the class Mirror. In Mirror's constructor, you determine its class name and
print it out:
public class Mirror
{
public static void main(String[] args)
{
Mirror myMirror = new Mirror();
}
public Mirror()
{
Class c = this.getClass();
String whoAmI = c.getName();
System.out.println(whoAmI);
}
}
So where would you want to use reflection? Well, suppose you want to make
a lightweight way to log messages within your application, and you want to see
what class the messages are coming from, but you don't want to clutter up your
classes with a lot of junk code. You would first create a new class to act as
the interface to your logging system and give it a static method that takes the
current object and a message string as an argument. This method can then look up
the class name, append the message, and write the log to the console or to a
file. The following code adds a new SimpleLogger class and changes your Mirror
class above to do exactly this:
public class Mirror
{
public static void main(String[] args)
{
Mirror myMirror = new Mirror();
}
public Mirror()
{
SimpleLogger.writeToLog(this, "Hello");
}
}
// SimpleLogger.java
public class SimpleLogger
{
public static void writeToLog(Object o, String message)
{
Class c = o.getClass();
String whoAmI = c.getName();
System.out.println(whoAmI + ": " + message);
}
}
The output of running this program is
Although this example is trivial and you would obviously want to use something far more robust for commercial-strength logging, it gives you an idea of some of the creative ways you can use reflection.
Creating Classes at Runtime Using Reflection
Creating classes at runtime has two steps. First, you
call a static method on the class called forName(), which takes a string as an
argument, which is the name of the class that the method will return. Second,
now that you have a reference to the type of Class that you would like to
create, you can call the static method getInstance() on this reference to return
your object. Note, however, that you will have to cast this object back to the
type of object you are creating. The following code is from the previous
example, but it's been modified to create the Mirror class using
reflection.
public class Mirror
{
public static void main(String[] args)
{
try
{
Class c = Class.forName("Mirror");
Mirror myMirror = (Mirror) c.newInstance();
}
catch (Exception e)
{
System.out.println(e);
}
}
public Mirror()
{
SimpleLogger.writeToLog(this, "Hello");
}
}
The addition of the try/catch block that protects the instantiation of
the new object here only catches the generic exception. The method forName() can
throw IllegalAccessException or ClassNotFoundException. In addition,
newInstance() can throw InstantiationException. Each of these three types of
exceptions can be caught separately in order to deal with them appropriately.
When you're first working with a new API, it is a good idea to
purposefully put something in your code that should generate an exception. That
way, you will be able to see if the API throws the type of exception you are
expecting. You can easily do this here by changing the "Mirror" in the
line
to the name of a class that does not exist. Then, recompile and run the
example. You should get a java.lang.ClassNotFoundException.
A Not-So-Simple Example
Actually, the following example is fairly
simple; it's just a good-sized chunk of code. The class below, ObjectRipper,
uses the reflection API to tell you just about everything you could possibly
want to know about an object:
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class ObjectRipper
{
public static void getObjectInfo(Object anObject)
{
getClassInfo(anObject);
getFieldInfo(anObject);
getMethodInfo(anObject);
}
public static void getClassInfo(Object anObject)
{
Class aClass = anObject.getClass();
String className = aClass.getName();
String classModifiers = Modifier.toString(aClass.getModifiers());
System.out.println("Class: " + className);
System.out.println(" Modifiers: " + classModifiers);
}
public static void getFieldInfo(Object anObject)
{
System.out.println("Fields");
Class aClass = anObject.getClass();
Field[] publicFields = aClass.getFields();
for (int i = 0; i < publicFields.length; i++)
{
String fieldName = publicFields[i].getName();
Class fieldClass = publicFields[i].getType();
String fieldClassName = fieldClass.getName();
String fieldModifiers = Modifier.toString(publicFields[i].getModifiers());
String fieldValue = "Not a primative";
if (fieldClass.isPrimitive() == true)
{
try
{
Object fieldObject = publicFields[i].get(anObject);
fieldValue = fieldObject.toString();
}
catch (IllegalAccessException e)
{
System.out.println(e);
}
}
System.out.println(" " + fieldName);
System.out.println(" " + "Modifiers: " + fieldModifiers);
System.out.println(" " + "Type: " + fieldClassName);
System.out.println(" " + "Value: " + fieldValue);
}
}
public static void getMethodInfo(Object anObject)
{
System.out.println("Methods");
Class aClass = anObject.getClass();
Method[] theMethods = aClass.getMethods();
for (int i = 0; i < theMethods.length; i++)
{
String methodName = theMethods[i].getName();
System.out.println(" " + methodName);
String returnType = theMethods[i].getReturnType().getName();
System.out.println(" Return Type: " + returnType);
Class[] parameterTypes = theMethods[i].getParameterTypes();
System.out.print(" Parameters:");
for (int k = 0; k < parameterTypes.length; k ++)
{
String parameterString = parameterTypes[k].getName();
System.out.print(" " + parameterString);
}
System.out.println();
}
}
}
Let's break this example down into more manageable chunks. First, you
will notice that this class is composed of four static methods:
getClassInfo()
getFieldInfo()
getMethodInfo()
Since all the methods are static, you will never need to instantiate
ObjectStripper. Instead, you can just call its methods directly by prefixing
them with the class name. Also, although you can call any of the four methods
directly, getObjectInfo() is provided as a convenience method, which in turn
calls the other three methods.
Let's look at getClassInfo(). The purpose
of getClassInfo() is to retrieve and print out the class name and modifiers for
the class in question. Class modifiers tell you whether the class is public,
protected, private, static, final, or abstract. It also tells you whether it's
an interface. Multiple modifiers are possible. Now, let's break down the method
line by line:
This line simply retrieves the class of the object in question. You will
do this in several places because most of the reflection calls deal with the
class of an object rather than the object itself.
String classModifiers = Modifier.toString(aClass.getModifiers());
Once you know the class, you can make a call to getName(), which returns
the name of the class as a string. Next, you call the method getModifier() on
the class, which returns an integer representation of the modifiers. You could
decode this directly yourself, but it is safer and more practical to call the
static toSting() method on the modifiers class, which gives you a nice string
representation of the modifiers without your having to be aware of their
internal integer values. In general, this is a good practice. For example, what
if the next release of the language includes new modifiers? If you don't follow
this practice, you will have to modify your code to check for them explicitly.
These two lines just format and print your results:
System.out.println(" Modifiers: " + classModifiers);
Next, take a look at getFieldInfo():
Class aClass = anObject.getClass();
Field[] publicFields = aClass.getFields();
First, you print out a label so that you have some context for the output
to follow. Then, as you did in getClassInfo(), you look up the class from the
object. Once you have the class of the object, you make a call to the
getFields() method, which returns an array of field objects, each of which
represents one public field of the object you are examining.
This for loop iterates over each of the field objects contained within
your object.
Class fieldClass = publicFields[i].getType();
String fieldClassName = fieldClass.getName();
String fieldModifiers = Modifier.toString(publicFields[i].getModifiers());
Within the for loop, you use the getName(), getType(), and getModifiers()
methods on each of the field objects.
if (fieldClass.isPrimitive() == true)
{
try
{
Object fieldObject = publicFields[i].get(anObject);
fieldValue = fieldObject.toString();
}
catch (IllegalAccessException e)
{
System.out.println(e);
}
}
Within the for loop operating on each of the field objects, examine each
field object to determine if it is a primitive type. If it is, grab the object
stored in the field object using a get() method and extract its value to print
out later using a toString(). Note that the try/catch block is required by the
get() method of the Field Object and may throw an
IllegalAccessException.
System.out.println(" " + "Modifiers: " + fieldModifiers);
System.out.println(" " + "Type: " + fieldClassName);
System.out.println(" " + "Value: " + fieldValue);
This final block of code within the for loop formats and prints your
results for each field object.
Now, take a look at the third and final
method, getMethodInfo().
Class aClass = anObject.getClass();
Method[] theMethods = aClass.getMethods();
The first three lines are almost identical to those of getFieldInfo(),
except that instead of an array of field objects, you get an array of method
objects.
As with the previous method, set up a for loop to iterate over your
array.
System.out.println(" " + methodName);
String returnType = theMethods[i].getReturnType().getName();
System.out.println(" Return Type: " + returnType);
Here, you make calls to the getName() and getReturnType() methods of each
method object and print the results.
System.out.print(" Parameters:");
for (int k = 0; k < parameterTypes.length; k ++)
{
String parameterString = parameterTypes[k].getName();
System.out.print(" " + parameterString);
}
System.out.println();
Since each method can have multiple parameters, make a call to
getParameterTypes() on each method object and then iterate over the array of
classes returned, printing the types of each as you go.
That wraps up how
the example works, so go ahead and compile it and try running a few of your own
objects through it, or grab some out of your favorite API. If you are even more
ambitious, you can extend the example to include constructors or list all of the
parent classes and interfaces that your object's class implements. Or how about
modifying the SimpleLogger Class to call getFieldInfo()? If you do this, you
might also want to make fieldValue print out Strings.
I think it's
interesting to note that if you changed the example to take classes as arguments
and added some HTML tags, you would have the basic framework for JavaDoc. The
fact that this basic framework fits in fewer than 100 lines of code speaks
volumes to the power of reflection.
These examples only scratch the
surface of the capabilities of reflection. It is easy to see how critical
reflection is to programmers writing Integrated Development Environments (IDEs)
and tools like JavaDoc, but what about the not-so-obvious places like JAX-RPC?
Because you can create objects at runtime based on their names in text strings
and invoke methods from text strings also, you can see how easy it is to convert
from an XML message to a method invocation on an object without using a
mile-long if/then/else statement.
Myths About Reflection
I would like to debunk two basic myths about reflection. The first myth is that reflection is used only by tool designers and doesn't really have a practical use for those of us not creating tools. Well, I think the general usefulness of these examples disproves this myth, and by this point, you may already be thinking about how you can incorporate reflection into your applications. The second myth is that reflection is slow. Like many myths, this one is partially grounded in the truth. Early versions of J2SE did suffer some from their implementation of reflection; however, as of J2SE 1.4, you will find that using reflection has no discernable performance implications.
Conclusion
Reflection is another of those truly great features--like automated memory management or WORA--that set Java apart from most other programming languages. Reflection allows you to examine and manipulate classes and objects at runtime. You can tell just about everything about a class, including its inheritance tree, constructors, public fields, and public methods. With objects, you can determine the class that instantiated them as well as the state of their public fields. You can also create classes at runtime, knowing nothing more about the class than its name as a string. Similarly, you can invoke methods on an object, based on knowing the name of a method as a string. You can easily combine these two capabilities to create objects and invoke methods as defined in text files or XML, thereby providing yourself with a simple and adaptable solution to a common computing problem.
Where to Go for More Information
If you would like more information on reflection,
you can read the whole 1.4.1
API from Sun. Or, for a more hands-on approach, you can work through the reflection API
track.
Michael J. Floyd is an extreme
programmer and the Software Engineering Technical Lead
for DivXNetworks.
He is also a consultant for San Diego
State University and can be reached at
LATEST COMMENTS
MC Press Online