Friday, August 22, 2014

Reflection in Java

Reflection is a way to inspect the internals of a class at runtime. It's a powerful feature but has to be handled with care. It in some sense breaks the notion of Object oriented concept. When we make a variable or method private we assume that the world is not able to see it. With reflection that's not true. With reflection one can inspect the internal details of a class and can even call methods and set attributes.

Let's see this with an example. Let's say we have a class that we want to reflect

public class ClassToReflect {
//No arg constructor
public ClassToReflect(){
System.out.println("No argument Constructor");
}

//Constructor with argument
public ClassToReflect(String arg){
privateVariable = arg;
System.out.println("Argument Constructor woth argument " + arg); 
}
private String privateVariable;
public String publicVariable;
private void privateMethod(){
System.out.println("Inside Private Method");
}
public void publicMethod(){
System.out.println("Inside Public Method");
}
}

Now let's see  how we can do a reflection on the above class. We will do the following in the code:
  • Get the class instance
  • Get the constructor and make and object/instance of the reflected class
  • Access public and private variables
  • Access public and private methods and invoke them.

//Getting the class 
Class classToReflect = ClassToReflect.class;
System.out.println("Class to Reflect: " + 
             classToReflect.getSimpleName());

//Get the constructor
Constructor[] constructors = 
  classToReflect.getConstructors();
for(Constructor c: constructors){
System.out.println("Constructor: " + c.getName());
Parameter[] parameters = c.getParameters();
for(Parameter p: parameters){
System.out.println("Parameter: " + p.getName());
}
}

//Creating an object using argument constructor
ClassToReflect objectOfClass = null;
try {
Constructor constructorWithArgument = 
classToReflect.getConstructor(String.class);
objectOfClass = 
   (ClassToReflect)constructorWithArgument.newInstance("Argument");
} catch (Exception e) {
//I am handling generic exception. However you might want to
//handle fine grained exception
e.printStackTrace();

//Get the variables. 
//Modifiers tell about the type of variable. In our case the modifier are
//PUBLIC -1
//PRIVATE - 2
//For details about modifiers see javadoc of Modifier. Modifier values 
//represent bit positions so that has to be decoded from that perspective.
//Calling getFields method will only return the public fields
Field[] fields = classToReflect.getDeclaredFields();

//Print fields list
for(Field f: fields){
System.out.println("Field: " + f.getName() +
", Type: " + f.getType() + 
", Modifier: " + f.getModifiers() );
}

//For private variable we can get the value of the variable by making an 
//object
try {
//Get the private field of object we constructed above
Field privateVariableField = classToReflect.getDeclaredField("privateVariable");
//To access the value, set accessible needs to be set true. Not a good 
//idea as this breaks the object oriented concepts
//Try commenting this and you will see an exception.
privateVariableField.setAccessible(true);
System.out.println("Value: " + privateVariableField.get(objectOfClass));
} catch (Exception e) {
System.out.println("Exception: " + e.getMessage());
}

//For methods. We will see only public method. getMethods will return
//all the methods from super class also
Method[] methods = classToReflect.getDeclaredMethods();

//Print methods list
for(Method m: methods){
System.out.println("Method: " + m.getName() + 
          ", Return Type: " + m.getReturnType() +
          ", Modifier: " + m.getModifiers());
Parameter[] parameters = m.getParameters();
for(Parameter p: parameters){
System.out.println("Parameter Type: " + p.getType());
}
}

//Invoking public method
try {
Method publicMethodToInvoke = classToReflect.getDeclaredMethod("publicMethod", null);
publicMethodToInvoke.invoke(objectOfClass,null);
} catch (Exception e) {
e.printStackTrace();

//Invoking private method
try {
Method privateMethodToInvoke = classToReflect.getDeclaredMethod("privateMethod", null);
//Need to set the accessibility as true
//Not a good idea
privateMethodToInvoke.setAccessible(true);
privateMethodToInvoke.invoke(objectOfClass,null);
} catch (Exception e) {
e.printStackTrace();

On running the above you will see the out put as follows:

Class to Reflect: ClassToReflect
Constructor: com.lalit.javaExamples.reflection.ClassToReflect
Constructor: com.lalit.javaExamples.reflection.ClassToReflect
Parameter: arg0
Argument Constructor woth argument Argument
Field: privateVariable, Type: class java.lang.String, Modifier: 2
Field: publicVariable, Type: class java.lang.String, Modifier: 1
Value: Argument
Method: publicMethod, Return Type: void, Modifier: 1
Method: privateMethod, Return Type: void, Modifier: 2
Inside Public Method
Inside Private Method

There are some downside to the reflection:
  • Reflection exposes the internals of a class. For example we can invoke private methods. This breaks the contract of object orientation and may conflict with security managers also
  • Reflection has performance penalty as lot of things has to be resolved at runtime.This is primarily because the compile time optimizations cannot be applied
Code on GitHub

No comments:

Post a Comment