Friday, August 17, 2007

Reflections on Java Reflection - part 2

As the example shows, getting the class name and super-class is as easy as calling some accessors. If you are interested in knowing if the class is public or abstract or final, the process is only slightly more complicated. All of this information comes packaged up in a single int returned by getModifiers. Fortunately, Java supplies you with the Modifier class, which is contains a bunch of static methods that will help you make sense of the number returned by getModifiers.


Calling getClass on an instance is not the only way to get hold of a class object. You can also get it directly from a class name:

Class klass = Employee.class;

The third way of getting hold of a class is the interesting one: you can create a Class object from a string -- well only if that string happens to contain the name of a class. The way to do this is to call the forName class method on Class:

Class klass = Class.forName("com.russolsen.reflect.Employee");

System.out.println( "Class name: " + klass.getName());
System.out.println(
"Class super class: " + klass.getSuperclass());

// Print out the rest...

One thing to keep in mind about forName is that you need to supply the full, complete with the package, name of the class. Plain old "Employee" will not do, it has to be "com.russolsen.reflect.Employee". With forName we begin to get a glimpse at some of the fundamental power (and coolness) of the reflection API: you can start with a class name in a string and end up with the class.
Instances Instantly

Getting hold of a class and finding all about it is interesting and useful in its own right, but actually doing something with it is where reflection gets really exciting. The most obvious thing to do with a Class object is to make a new instance of that class. The simple way to do this is with the newInstance method. To show all this in action, the little program below takes a command line argument (which should contain a class name) creates a class from it and then makes a new instance of that class:

package com.russolsen.reflect;

public class NewInstanceExample
{
public static void main(String[] args)
throws ClassNotFoundException,
InstantiationException, IllegalAccessException
{

Class klass = Class.forName(args[0]);
Object theNewObject = klass.newInstance();
System.out.println("Just made: " + theNewObject);
}
}

Run the code above with "com.russolsen.reflect.Employee" as an argument and you will manufacture a brand new Employee object:

Just made: Employee: John Smith 50000

Run it again, but this time feed it "java.util.Date" and you will get:

Just made: Tue Feb 27 20:25:20 EST 2007

Think about how much flexibility you get with just a few lines of code: the program above really knows nothing about Employee or Date and yet it can create a new instance of either. This is a different way of doing Java.
Beyond the no-args Constructor

It turns out that calling the Class.newInstance method is identical to doing an ordinary new with no arguments. But what happens if you call newInstance on a class that lacks a no argument constructor? Nothing good: you are in for an unpleasant InstantiationException.

The good news is that you can dynamically create new instances of a class that requires constructor arguments -- you just have to work a bit harder. What you need to do is to find the constructor that you need from the class and call it with the right arguments. The way to find the constructor of your dreams is to call the getConstructor method with a description of the constructor that you are looking for. What you will get back is a Constructor object, which you can use to create a new instance.

Let's see how this all works in code:

Class klass = Class.forName("com.russolsen.reflect.Employee");

Class[] paramTypes = {
String.class,
String.class,
Integer.TYPE };

Constructor cons = klass.getConstructor(paramTypes);

System.out.println( "Found the constructor: " + cons);


Object[] args = {
"Fred",
"Fintstone",
new Integer(90000) };

Object theObject = cons.newInstance(args);
System.out.println( "New object: " + theObject);

The only difference between the constructors are the parameters they take, so the way that you tell getConstructor which constructor you are looking for is to pass it an array of Class's, one for each parameter. The example above goes looking for a constructor that takes two strings and an int. Once you have a Constructor, creating a new object instance with it is very simple: you just call the newInstance method, passing in another array, this time an array full of the actual argument values.

No comments: