A class, as we understand it, is basically a “capability set,” an entity type that agrees to abide by a contract of what it is capable of. Now, the capability of any class must be known before it can be used in a program. The capability is “published” in the form of method signatures of the class and also the “type” of accessible “fields.”
In Java, this knowledge of the capabilities of the class being used in a program is enforced at compile time, javac checks and verifies this capability set very thoroughly. How is this achieved? Obviously javac has a mechanism to read and understand a class and find out the implication of the messages between objects. For example, there could be a method in a class, say Callee:
class Callee {
.
.
public void doIt () throws CantDoException {
}
….
}
And in a class, say Caller, you have an object of Callee and the method
doIt() |
class Caller {
Callee callee;
…..
callee.doIt();
…..
}
Assuming that CantDoException is not derived from RuntimeException, the compiler will obviously complain that CantDoException should either be caught or declared in the throws clause of the method from within which the
doIt() |
The next logical question is: Can we find out the capability at runtime of a class whose capabilities were not known at compile time? A program that analyzes the capabilities of classes is referred to as being reflective. Java has a package, java.lang.reflect, that has classes that support this reflection, along with some classes in java.lang, the most notable of which is the java.lang.Class class. These classes support creation of programs such as debuggers, interpreters, object analyzers, WYSIWYG GUI tools and so on.
How many times while programming for applications other than those mentioned above do you need to use reflection? I reckon not much, but sometimes when you need to work on a class that was not known at compile time, reflection provides the answer.
The JavaBeans framework makes use of this reflection mechanism to actually plug in a bean to a container of beans or a beanbox.
Let me liven up the proceedings with an example. Suppose there is a great program that is running around the clock on a server and is collecting data about possible extraterrestrial life, it is continuously collecting data and cannot be brought down, suppose there is a small piece of an algorithm that plays a role in that program. Scientists find out a way to optimize that algorithm and thus save time, but the program cannot be brought down. If the early designers of the program were thoughtful enough, then there is a way to dynamically plug in the brand new algorithm.
Now, if the algorithm implemented an interface Algorithm
interface Algorithm {
public void process();
}
and if we have the reference to the algorithm as this interface type, then we could easily swap the Algorithm implementations after reading it at runtime, like:
Algorithm algo;
.
.
String className = ConsoleReader.readLine(); // read from console
algo = (Algorithm) ((Class.forName(className)).newInstance());
Let’s say “algo” is the name of a variable of type Algorithm (interface) and this reference is used to call the method
process() |
forName(className) |
Class.forName(String) |
newInstance() |
You can actually code separately (from a separate terminal window, compile and provide the name to our Star Trek program in DynamicLoad.java), and bingo, it gets into operation immediately.
Going back to our original discussion, is this loading really dynamic, in the sense that the new class really was not known at compile time? The answer is no. We imposed a restriction on the behavior by restricting our interaction through the Algorithm interface, which was known at compile time; in fact, we made use of polymorphism apart from dynamically loading the class. Even in the case of JavaBeans, while using introspection, we enforce a design pattern of getter and setter methods to find out the properties and their kind (read/write or read-only).
Using the class Class methods like
getMethod() |
getMethods() |
getInterfaces() |
getConstructors() |
getField() |
Suppose our
process() |
myInnovation() |
Method m[];
Class c = Class.forName(classname);
m = c.getDeclaredMethods();
for(int i=0;i<m.length;i++) {
if(m[i].getReturnType().getName().equals(“ReturnValue”) && m[i].getParameterTypes().equals(“InputParam”) )
{
rtObj = (ReturnValue) (m[i].invoke(c.newInstance(), ipObj));
Now this is not compilable, because
getParameterTypes() |
Class[] |
As a matter of fact the new Dynamic Proxy API with JDK 1.3 makes use of a similar searching mechanism to arrive at the functionality of having a dynamic proxy. Let me explain this, the API allows us to create a Proxy object, which is told about certain interfaces that it has to handle; then in an application an instance of this Proxy object is created, and calls meant to be handled by implementations of these interfaces are made to this Proxy object. This Proxy object is also told about an InvocationHandler, which contains the actual delegate object to which the call is finally made. Sounds cryptic! Let me clarify this with an example. InvocationHandler is actually an interface that has a single method:
public Object invoke (Object proxy, Method m, Object[] args) throws Throwable
Here, the proxy is the reference to the Proxy object created. This
invoke() |
public class MyHandler implements java.lang.reflect.InvocationHandler {// this is the delegate object, can be any user defined object
Object delegate;public MyHandler (Object obj) {
this.delegate = obj ;
}public Object invoke (Object proxy, Method m, Object[] args) throws Throwable {
try {// may actually invoke on the delegate here and also do something before or after
}
catch(InvocationTargetException e){
throw e.getTargetException();
}
catch(Exception e) {
throw e;
}
// return here
}
Now, assume that the delegate object implements three interfaces — A, B, and C — the dynamic proxy API enables you to create a Proxy object with a type of any of the interfaces. For example:
A a = (A) java.lang.reflect.Proxy.newInstance (ClassLoader cl, Class[] { A.class, B.class,C.class }, new MyHandler(delegate) );
Or the same can also be assigned to reference of type B or C. When the client says “
a.doSomethig() |
doSomething() |
newProxyInstance() |
invoke() |
This, I presume, is the best we can do in getting to dynamic pluggability of classes at runtime; the underlying idea being that some form of contract or some design pattern has to be adhered to. Reflection APIs actually take us very close to this goal.
About the Author
Nasir Khan is a Sun Certified Java programmer, with a B.E. in electrical engineering and a masters degree in systems. His areas of interest include Java applications, EJB, RMI, CORBA, JDBC and JFC. He is presently working with BayPackets Inc. in high-technology areas of telecom software.