Learn Java Programming
11.0 Reflection in Java
The following are the objectives of this module:
- To understand the Reflection APIs in Java
- Getting the type of the object using Reflection
- Getting the constructors
- Getting the fields inside an object
- Invoking a method using Reflection
We know that Java compiles the code first to byte codes. It is the byte code that is interpreted into machine code by the Java Virtual Machine. By virtue of this arrangement, it is possible for us to look inside the byte code for any object and determine its Type, its Constructors, its Fields, and its Methods.
It is even possible for us to alter the existing behaviors and add new behaviors to an object. This incredibly powerful feature is made available to us by Java Reflection.
Looking inside an object
It is possible and perfectly legal for us to look inside an object!
Let us assume we have an object called “obj”. We do not know any other information about the object till now. We can obtain the Type of the object, first by calling the getClass() method and then the getName() method on the Class object.
Class cls = obj.getClass();
String className = cls.getName();
Getting the fields associated with a class
Once we have the class object, it is now easy to get the properties or attributes that have been defined in the class. The getDeclaredFields() method on the Class objects gives us an array of Field objects
. . .
Class cls = obj.getClass();
. . .
Field[] fields = cls.getDeclaredFields();
for (int i=0; i < fields.length; i++) {
Field field = fields[i];
field.setAccessible(true);
Object value = field.get(obj);
System.out.println(“Field name is “
+ field.getName() + “ value is “ + value);
}
While getting the field values, we first have to set the accessible value to true, using the setAccessible() method on the field object. Calling the get() method on the Field object, fetches the value of that field from the given object.
Getting to know the methods in a class
Reflection allows us to get the information about the methods inside the object. The getDeclaredMethods() on the Class object, will return an array of Method objects.
Class cls = obj.getClass();
Method[] methods = cls.getDeclaredMethods();
for (int i=0; i < methods.length; i++ ) {
Method method = methods[i];
System.out.println(method.getName());
Class paramTypes[] = method.getParameterTypes();
for (int j = 0; j < paramTypes.length; j++) {
System.out.println("Parameter [" + j
+ "] type is [" + paramTypes[j] + "]");
}
System.out.println( " Returns: " + method.getReturnType() );
}
We get all the methods declared in a class using the getDeclaredMethods() on the class object. The getParameterTypes() method on the method object will give us the Types of the IN parameters in their order of occurrence. The return type of the method can be obtained, by calling the getReturnType() method.
Getting to know the constructors
It is also possible to fetch the constructors associated with the class using the getConstructors() method:
Class cls = obj.getClass();
Constructor[] constructors = cls.getConstructors();
Class paramTypes[] = constructors.getParameterTypes();
Simple Object XML Generator - Putting Reflection to use
Reflection does really give us a lot of power to access the Type and State information of any object. Now let us put our knowledge of XML programming and try to retrieve the state of any given object in the form of an XML representation.
Let us design an XMLGenerator class, that will take any object and output the state of the object as an XML data. The following are the methods needed for building the XML Generator:
private static String emitXMLHeader(Object obj) {
Class cls = obj.getClass();
return "<" + cls.getName() + ">";
}
private static String emitXMLFooter(Object obj) {
Class cls = obj.getClass();
return "" + cls.getName() + ">";
}
private static String emitXML(String name, Object value) {
StringBuffer strBuf = new StringBuffer();
strBuf.append("<" + name + ">");
strBuf.append(value.toString());
strBuf.append("" + name + ">");
return strBuf.toString();
}
The emitXMLHeader() and the emitXMLFooter() methods with output the beginning and end tags for the XML - we will use the Type name for the XML begin and end tags.
For example, if the object is of Type com.itseasytolearn.model.Student, then the XML start and end tags will be as follows:
<com.itseasytolearn.model.Student>
. . .
</com.itseasytolearn.model.Student>
Here is the code that actually emits the XML
public static String generateXML(Object obj) throws Exception {
StringBuffer strBuf = new StringBuffer();
Class cls = obj.getClass();
strBuf.append(emitXMLHeader(obj));
Field[] fields = cls.getDeclaredFields();
for (int i=0; i < fields.length; i++) {
Field field = fields[i];
field.setAccessible(true);
Object subObj = field.get(obj);
if (!Modifier.isStatic(field.getModifiers())) {
if ( field.getType().isPrimitive() ||
"java.lang.String".equalsIgnoreCase(
field.getType().getName())) {
strBuf.append(emitXML(field.getName(), subObj));
} else if ("java.util.Date".equalsIgnoreCase(
field.getType().getName())){
strBuf.append(emitXML(field.getName(), subObj));
} else {
strBuf.append(generateXML(subObj));
}
}
}
strBuf.append(emitXMLFooter(obj));
return strBuf.toString();
}
Here we get all the non-static fields associated with the object. We then check if the field type is a primitive "field.getType().isPrimitive()". If it is a primitive data-type (int, float, double etc.) or if it is a java.lang.String type, then we emit the XML for the field name and value.
If the field is a sub-object, we recurse continously (call the same method again and again till we reach a Primitive or a String or a java.util.Date object.
Invoking a method using Reflection
We can invoke any method using Reflection, without actually calling it from the object. This can be acheived by using the invoke() method on the Method class.
The invoke() method needs two parameters – the values to pass to the method as an Oject[] array, and the object where the method has to be invoked!
. . .
Class c = Class.forName("Person");
Class parameterTypes[] = new Class[1];
parameterTypes[0] = String.class;
Method m = c.getMethod("setAddress", parameterTypes);
Object params[] = new Object[1];
params[0] = "10 Downing Street, London";
m.invoke(obj, params);
We first need to get the method with the given name and signature. Here we are looking for the method called "setAddress" that takes a String as an input parameter.
Method m = c.getMethod("setAddress", parameterTypes);
Once we have the method object, we pass in an Object[] and execute the method on the given object.
The following class will demonstrate how to call a method on an object using only Reflection.
import java.lang.reflect.Method;
import java.util.Date;
public class TestInvoke {
public static void main(String[] args) throws Exception {
Person primeMinister = new Person();
primeMinister.setAddress("1 Safdarjung Road, New Delhi, India");
System.out.println(primeMinister);
Object target = primeMinister;
Class c = Class.forName("Person");
Class parameterTypes[] = new Class[1];
parameterTypes[0] = String.class;
Method m = c.getMethod("setAddress", parameterTypes);
Object params[] = new Object[1];
params[0] = "10 Downing Street, London, United Kingdom";
m.invoke(target, params);
System.out.println(target);
}
}
Note how we change the address from "1 Safdarjung Road, New Delhi, India" to "10 Downing Street, London, United Kingdom" without calling the setAddress() method directly on the object.
Summary:
Using Java Reflection, it is possible to obtain a wealth of information about the objects. Java Reflection APIs are very much useful in building libraries and frameworks where the Type of the object cannot be known in advance. Using Java Reflection, it is thus possible
- To get information about the Type of the object
- The fields, methods and constructors defined on the object
- Invoke a method using reflection