Have you ever wondered how your favorite IDE manages auto-completion of
method and field names? Even better, how does an object relational (OR) mapping
framework like Hibernate support default fields while persisting entity classes?
If all these questions sound geek, you must give the Reflection API a spin. Much
of the sizzle that has been caused by Java is because of power added by the
Reflection API to change the runtime behavior of an application that's running
inside a Java Virtual Machine (JVM). In this article, we'll look at the runtime
magic that Reflection provides to Hibernate O/R mapping framework and then we'll
use the Java Reflection API to break one of the fundamental rules of the Java
programming language--we'll access a method marked with the private access
modifier from the client code.
Hibernate mapping
To begin with, we'll look at Hibernate OR mapping framework. As an
illustration, consider the following mapping file which declares the persistent
properties of an entity class:
|
How does Hibernate get to know the Java type of the name and address
properties? How does Hibernate instantiate the Patient Plain Old Java Objects (POJO)
entity class? What's the significance of the default-access=field element? All
this and much more are possible through Reflection. Hibernate can access the
fields (including private) directly. Hibernate will try to determine the correct
conversion and mapping type itself without specifying the type attribute in the
mapping. Further, Hibernate recommends that the access of the default 'no
argument' constructor should be at least package level, as Hibernate needs to
instantiate the class through Reflection.
Jargon watch
|
Accessing private method using Reflection
According to Java Language Specification (JLS), 'A private class member or
constructor is accessible only within the body of the top level class that
encloses the declaration of the member or constructor.' This is a well known
Java rule that once a method or a field is declared with the private modifier,
it can't be accessed by any other class. But with Reflection, breaking this
fundamental rule is simple. Reflection provides a convenient handle to all
fields and methods irrespective of their accessibility.
We'll design a simple Java stack of patients as a part of our MedTracker
application to provide a LIFO implementation. Apart from the conventional push()
and pop() methods we'll provide an overloaded version of pop() method which
accepts an integer argument. The integral value specifies the number of elements
to be popped from the stack. As an illustration, consider the following code
listing which presents the patient stack:
private Patient<> pop(int elementCount) {
System.out.println("Inside private method
pop(int)");
if (elementCount < 0)
throw new IndexOutOfBoundsException();
Patient newPatients<>=new Patient
for(int i=0;i
return newPatients;
}
This code fragment is pretty straight forward. Inside the pop(int
elementCount) method, we've put a simple check for the elementCount argument;
for any negative value passed to this method, we propagate an
IndexOutOfBoundsException (an unchecked exception). For any positive value, we
populate a local array of Patient objects and return the same. Next, we'll call
the private pop(int elementCount) method from a standalone Java client through
Reflection.
Java Reflection API lets you hook on to private objects and tweak their behavior at run time. The pop() method appears to remove the given range of items from the stack |
Putting Reflection in action
It's time to hit the ground running! We'll write a simple standalone Java
client which will use the reflection API to call the private pop(int
elementCount) method on the stack class. The following code fragment presents
the Stackmain.java class:
package com.pcquest.medtracker.client;
import com.pcquest.medtracker.model.Patient;
import com.pcquest.medtracker.model.Stack;
import java.lang.reflect.*;
public class Stackmain {
public static void main(String<> args)throws
Exception {
Stack s=new Stack(10);
s.push(new Patient("Samuel","Hoffman"));
s.push(new Patient("Richard","Ryan"));
s.push(new Patient("Steve","Beytel"));
System.out.println("The original list is: " +
s);
Class klass = s.getClass();
Class<> paramTypes = { Integer.TYPE };
Method m = klass.getDeclaredMethod("pop",
paramTypes);
Object<> arguments = { new Integer(2) };
m.setAccessible(true);
m.invoke(s, arguments);
System.out.println("The new list is: "
+ s);
}
}
In the Stackmain class we begin by instantiating the stack initially with 10
elements. Next, we get a Class object. The arguments to any method that is
invoked through reflection is passed as an array of Class object. The
getDeclaredMethod returns all methods declared by one class. In the above code
fragment, we get the private pop(int elementCount) method handler. Then we call
the setAccessible() method, passing 'true' to make it available to the caller
program. Finally, the invoke() method invokes the underlying method represented
by the Method object, on the specified object with specified parameters. The
following screenshot depicts the output.
Conclusion
Reflection is a powerful tool in the hands of a developer with which she can
modify the runtime behavior of applications running in the Java virtual machine.
That said; reflection should only be used by experienced developers. According
to the Sun Java Tutorial, “Reflection is powerful, but should not be used
indiscriminately”. Reflection comes at a cost--non-reflective programs are more
responsive. A security manager can prevent reflexive behavior at runtime. As a
thumb rule, Reflection should only be used by experienced developers as
indiscriminate usage could change semantics of a running application and
introduce side effects.