Thursday, April 5, 2012

Reflection in Java

Dear reader,
  I am discussing here "Java's Reflection API". Points discussed are below:
        a. Introduction and Use of Java's Reflection API.
        b. Getting the complete details of a Class using Reflection API and Complete program to use that 
           and so output.
        c. Call the Method using reflection and setting the values (it is there in program itself).
        d. How to find Getter/Setter method using Java Reflection API.
        e. How to call a Private method of a class using Reflection API. Again complete program is given.
        f. How to create Arrays using Reflection and how to find an Object if it is an Array. Also how to
           convert an Array into java.lang.Class.

Introduction:
It helps us to inspect classes, interfaces, fields and methods at runtime, without knowing the names of the 
classes, methods etc at compile time. It is also used to instantiate new objects, invoke methods and get/set 
field values. 

Using Java Reflection you can inspect Java classes at runtime. Inspecting classes is often the first thing 
you do when using Reflection. From the classes you can obtain information about:
    Class Name
    Class Modifies (public, private, synchronized etc.)
    Package Info
    Super class
    Implemented Interfaces
    Constructors
    Methods
    Fields
    Annotations
At the same time you can invoke constructors, methods using reflection. I have written a very good program on
this which covers one by one all most all topics. Please go through carefully.

Complete Program:
==========PersonClass.java=============
//This class is having all the field, method declarations. It is inside a package "com.modi.deepak".

package com.modi.deepak;
public class ParentClass {
    static int no_numerics=10;
    public int no_states=28;
    private static int no_chars=26;
    public int arr[]={0,1,2,3,4};
    
    /*private ParentClass(){
        System.out.println("ParentClass's default constructor");
    }*/
    public ParentClass(){
        System.out.println("ParentClass's default constructor");
    }
    private ParentClass(int no_numerics){
        System.out.println("ParentClass's integer parameterized constructor");
    }
    public ParentClass(String no_numerics){
        System.out.println("ParentClass's String parameterized constructor");
    }
    public void myMethod(){
        System.out.println("PersonClass myMethod with no parameter");
    }
    public static String myMethod(String name){
        System.out.println("PersonClass myMethod with String parameter");
        return "Hello:" +name;
    }
}

==========MainClass.java=============
//This is the main program which I need to run to see the output, no package, in default folder.

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import com.modi.deepak.ParentClass;  //importing ParentClass

public class MainClass {
    public static void main(String[] args) {
        System.out.println("Inside main class, main method...");

        Class classObject=ParentClass.class;
        System.out.println("Class's Full Name: "+classObject.getName());
        System.out.println("Class's Simple Name: "+classObject.getSimpleName());
        System.out.println("Count of modifiers: "+classObject.getModifiers());

        Package packageObject = classObject.getPackage(); //Gives you package name.
        System.out.println("Class's Package Name: "+classObject.getPackage());

        Class superclass = classObject.getSuperclass();  //Gives you super class name
        System.out.println("Class's Super class Name: "+superclass);

        Class[] interfaces = classObject.getInterfaces(); //Gives all interfaces class implements.
        Constructor[] constructors = classObject.getConstructors(); //Gives all public Constructors class has.

        Method[] methodObject= classObject.getMethods(); //Gives all methods class has.
        System.out.println("\nMethods of this class:");
        for(Method m:methodObject)
            System.out.print(m.getName()+", ");

        System.out.println("\n"); //For new line
        Field[] fieldObject = classObject.getFields();  //Getting all field objects which are public.
        System.out.println("Public field length: "+fieldObject.length);  //Only 2
        for(Field m:fieldObject)
            System.out.print(m.getName()+", ");  //Only no_states, arr, as they are public

        Field[] allFieldObject = classObject.getDeclaredFields();  //Getting all fields including private.
        System.out.println("\nAll field length: "+allFieldObject.length);  //Total 4
        for(Field m:allFieldObject)
            System.out.print(m.getName()+", ");  //all fields including private

        //Fetching Annotations
        Annotation[] annotations = classObject.getAnnotations();


        //Selected methods
        try {
            Constructor constructor = classObject.getConstructor(new Class[]{String.class}); //Only public constructor
            System.out.println("\nSelected Constructor: "+constructor);
        }
        //If no constructor matches the given constructor arguments, in this case String.class, 
        //a NoSuchMethodException is thrown. 
        catch(NoSuchMethodException e){
            e.printStackTrace();
        }

        //Instantiating an Object using reflection (constructor) only private constructor
        try {
            Constructor constructor = ParentClass.class.getConstructor(String.class);
            System.out.println("\nCreating an object using Reflection, only public constructor be invoked:");
            //Below argument is optional but need to provide String as we want to invoke String
            //Parameterized constructor
            ParentClass myObject = (ParentClass)constructor.newInstance("This String is optional");
            System.out.println("See above line output, constructor invoked..");
        }
        catch(NoSuchMethodException e){
            e.printStackTrace();
        }
        catch(InstantiationException e){
            e.printStackTrace();
        }
        catch(IllegalAccessException e){
            e.printStackTrace();
        } 
        catch (IllegalArgumentException e) {
            e.printStackTrace();
        } 
        catch (InvocationTargetException e) {
            e.printStackTrace();
        }


        //Setting field values
        try {
            Field field = classObject.getField("no_states");  
            ParentClass objectInstance = new ParentClass();  //Getting the value using normal new keyword.
            int value = (Integer)(field.get(objectInstance));  
            System.out.println("\nOld value from Class: "+value);
            value+=20;   //value can't be changed for final fields like no_states should not be final. 
            field.set(objectInstance, value);
            System.out.println("Modified value from Class: "+value);
        }
        catch(Exception e){
            e.printStackTrace();            
        }



        //Fetching and invoking methods dynamically
        try {
            Method method = ParentClass.class.getMethod("myMethod", String.class);
            //Method method123 = classObject.getMethod("myMethod", null);  //In case no parameter in method
            Object returnValue = method.invoke(null, "Deepak");
            
            ParentClass objectInstance = new ParentClass();  //Getting the value using normal new keyword.
            Object returnValue2 = method.invoke(objectInstance, "Deepak Modi");
            
            //The null parameter is the object you want to invoke the method on. If the method is 
            //static you supply null instead of an object instance. In this example, if 
            //myMethod(String.class) is not static, you need to supply a valid PersonClass instance. 
            System.out.println("\nReturned value after method call: "+returnValue);
            System.out.println("\nReturned value after method call: "+returnValue2);
        } 
        catch (SecurityException e) {
            e.printStackTrace();
        } 
        catch (NoSuchMethodException e) {
            e.printStackTrace();
        } 
        catch (IllegalArgumentException e) {
            e.printStackTrace();
        } 
        catch (IllegalAccessException e) {
            e.printStackTrace();
        } 
        catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}
    
//Output:
Inside main class, main method...
Class's Full Name: com.modi.deepak.ParentClass
Class's Simple Name: ParentClass
Count of modifiers: 1
Class's Package Name: package com.modi.deepak
Class's Super class Name: class java.lang.Object

Methods of this class:
myMethod, myMethod, getClass, hashCode, equals, toString, notify, notifyAll, wait, wait, wait, 

Public field length: 2
no_states, arr, 
All field length: 4
no_numerics, no_states, no_chars, arr, 
Selected Constructor: public com.modi.deepak.ParentClass(java.lang.String)

Creating an object using Reflection, only public constructor be invoked:
ParentClass's String parameterized constructor
See above line output, constructor invoked..
ParentClass's default constructor

Old value from Class: 28
Modified value from Class: 48
PersonClass myMethod with String parameter
ParentClass's default constructor
PersonClass myMethod with String parameter

Returned value after method call: Hello:Deepak

Returned value after method call: Hello:Deepak Modi
----------------------------------------------------------

Finding Setter and Getter method:
Apart from these fetching strategy, you can find a Setter/Getter methods using Reflection too,
Below is the code for the same:

    public static void printGettersSetters(Class aClass){
        Method[] methods = aClass.getMethods();

        for(Method method : methods){
            if(isGetter(method)) System.out.println("getter: " + method);
            if(isSetter(method)) System.out.println("setter: " + method);
        }
    }

    public static boolean isGetter(Method method){
        if(!method.getName().startsWith("get"))      return false;
        if(method.getParameterTypes().length != 0)   return false;  
        if(void.class.equals(method.getReturnType()) return false;
        return true;
    }

    public static boolean isSetter(Method method){
        if(!method.getName().startsWith("set")) return false;
        if(method.getParameterTypes().length != 1) return false;
        return true;
    }
------------------------------

NOTE: Generally there are two kind of methods available in reflection like "aClass.getMethods()" and 
      "aClass.getDeclaredMethods()". "getDeclaredMethods()" will give all the methods description like private, 
      protected, public, default but "getMethods()" will give only public method descriptions.
      So all declared methods like "getDeclaredFields(), getDeclaredConstructors(), getDeclaredMethods()" etc are available.
------------------------------

Accessing Private Methods from a class using Reflection:
-----------
//This is the class whose private method I want to invoke
package com.modi.deepak;
public class PrivateClass {
    private String value="Temp Value";
    private PrivateClass(){
        System.out.println("PrivateClass's private constructor");
    }
    public PrivateClass(String param){
        System.out.println("PrivateClass's public String parameterized constructor");
        this.value=param;
    }
    private String myMethod(){
        System.out.println("\n******PrivateClass private myMethod with no parameter******\n");
        return "Hello: " +value;
    }
    public void myAnotherMethod(String name){
        System.out.println("PrivateClass public myAnotherMethod with String parameter:"+name);
    }
}
-----------
//Main program who will call the above class
import java.lang.reflect.Method;
import com.modi.deepak.PrivateClass;

public class MainPrivateClass {
    public static void main(String[] args) throws Exception { //Throwing all exceptions here
        PrivateClass normalObject = new PrivateClass("Deepak Modi is good technical developer");
        
        normalObject.myAnotherMethod("Java is Ocean");
        //normalObject.myMethod();  //This is private method, not visible outside, Compiler won't allow it.

        Class privateObject=PrivateClass.class;
        Method privateMethod = privateObject.getDeclaredMethod("myMethod", null);
        
        //Making private method accessibility true
        privateMethod.setAccessible(true);

        String returnValue = (String)privateMethod.invoke(normalObject, null);
        System.out.println("Private method invoked, see above line output: returnValue = " + returnValue);
    }
}
-----------
//Output:
PrivateClass's public String parameterized constructor
PrivateClass public myAnotherMethod with String parameter:Java is Ocean

******PrivateClass private myMethod with no parameter******

Private method invoked, see above line output: returnValue = Hello: Deepak Modi is good technical developer

=================================
Creation of Array using Reflection: We can create an Array using Reflection.
Please see the program below. The most challenging part is to convert an Array (integer or String or anything else)
into a "java.lang.Class" object via Reflection. See below:

    Class intArray = Class.forName("[I");

The JVM represents an int via the letter I. The [ on the left means it is the class of an int array. This works 
for all other primitives too. For objects you need to use a slightly different notation:
   
   Class strArrayClass = Class.forName("[Ljava.lang.String;");
Notice the [L to the left of the class name, and the ; to the right. This means an array of objects with the given type.
If you use the below line of code: 
   Class c1=Class.forName("java.lang.String"); //It will not be an Array.

Please go through the complete coding below:   

---------------
import java.lang.reflect.Array;
public class ArrayReflection {
    public static void main(String[] args) {

        int arr[]=(int[])Array.newInstance(int.class,5);
        System.out.println("Newly Created Array length: "+arr.length);
        arr[0]=0;
        arr[1]=1;
        arr[2]=2;
        arr[3]=3;
        arr[4]=4;
        System.out.println("Array elements are: ");
        for(int num:arr)
            System.out.print(num+", ");

        //Now If I have an array, need to find what kind of Array it is:
        String[] strArray = new String[4];
        Class strArrayClass = strArray.getClass();
        //Class strArrayClass = String[].class;   //This and above line, both are same

        Class strArrayComponentType = strArrayClass.getComponentType();
        System.out.println("\nType of Array: "+strArrayComponentType);

        try {
            Class c=Class.forName("java.lang.Integer");
            System.out.println("Class: "+c+", Type: "+c.getComponentType()+", isArray: "+c.isArray());

            Class c1=Class.forName("java.lang.String");
            System.out.println("Class: "+c1+", Type: "+c1.getComponentType()+", isArray: "+c1.isArray());

            Class c2=Class.forName("[I");  //For Integer Array
            System.out.println("Class: "+c2+", Type: "+c2.getComponentType()+", isArray: "+c2.isArray());
            
            Class c5=Class.forName("[Ijava.lang.Integer");  //For Integer Array, complete path here.
            System.out.println("Class: "+c5+", Type: "+c5.getComponentType()+", isArray: "+c5.isArray());

            Class c3=Class.forName("[Ljava.lang.String;"); //For String Array
            System.out.println("Class: "+c3+", Type: "+c3.getComponentType()+", isArray: "+c3.isArray());
            
            Class c4=Class.forName("[Ljava.lang.Long;"); //For Long Array
            System.out.println("Class: "+c4+", Type: "+c4.getComponentType()+", isArray: "+c4.isArray());
        }
        catch(Exception e){
            e.printStackTrace();
        }
    }
}
---------------
//Output:
Newly Created Array length: 5
Array elements are: 
0, 1, 2, 3, 4, 
Type of Array: class java.lang.String
Class: class java.lang.Integer, Type: null, isArray: false
Class: class java.lang.String, Type: null, isArray: false
Class: class [I, Type: int, isArray: true
Class: class [I, Type: int, isArray: true
Class: class [Ljava.lang.String;, Type: class java.lang.String, isArray: true
Class: class [Ljava.lang.Long;, Type: class java.lang.Long, isArray: true


That's it. In next article I will write about Reflection for Generics, Proxy Objects.
However I found a good URL to read for, you can go through it, in fact few Ideas I have taken from the same URL:
http://tutorials.jenkov.com/java-reflection/generics.html
http://tutorials.jenkov.com/java-reflection/dynamic-proxies.html

Other useful links are below:
http://www.xyzws.com/javafaq/how-to-use-reflection-to-call-methods-in-java/153
http://onjava.com/pub/a/onjava/2007/03/15/reflections-on-java-reflection.html?page=1
http://www.wikijava.org/wiki/Class_and_static_Method_Reflection_example
---------------------------------END-----------------------------------      

No comments:

Post a Comment