Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Java / JavaSE / J2SE4

Loading the Class Information using Reflection

4.33/5 (2 votes)
25 Mar 2014CPOL3 min read 8.1K  
This tip will provide the basics and one place use of reflection in Java.

Introduction

The tip and the code inside will help a beginner who is trying to learn reflection understand the importance of reflection.

Background

Reflection is to load the classes and use their functionality by simple named loading and calling. Reflection is used in most of the frameworks for IoC. The reader should have basic knowledge about Java syntax and class libraries.

Using the Code

The code in the tip can be used to load any of the variables, methods or constructors and access them. We will be taking a look at each of them individually:

  1. Loading a class
  2. Accessing the constructors
  3. Accessing the fields
  4. Accessing the methods
  5. Accessing the annotations

Loading a Class

For loading a class, a static method forName(String fullySpecifiedName) of the "Class" class should be used. This method loads the class specified from the class path of the executing code and throws a ClassNotFoundException in case the class was not found in the class path. If you are familiar with the type 4 JDBC calls, you must have used this method in order to load the drivers. I think after reading this tip, it will give you some idea about how the loaded driver is used in database connectivity.

Java
try{
    String fullySpecifiedName = "reflections.TargetedForReflection"
    
    //Parameterized generics are advised to be used according to the best practices
    Class<? extends Object> c = Class.forName(fullySpecifiedName?);
    
    //Retrieving the canonical name of the loaded class
    System.out.println("Canonical name for the class is: " + c.getCanonicalName());
    
    //Retrieving the super class of the loaded class
    System.out.println("Super class for the class is: " + c.getSuperclass()); 
    
    //2. Accessing constructor using reflection
} catch(ClassNotFoundException cnfe){
    //Handle the exception
    cnfe.printStackTrace();
}  

Accessing the Constructors

The constructors' information of the loaded class or type can be loaded and any of the constructors can be used to create an instance of the class. An array of Constructor class can be retrieved from the class instance using the getConstructors() method. All the constructors can be loaded of the loaded type as follows (The above class loader code continues):

Java
//2. Accessing constructor using reflection
//Loading the constructors for the loaded class
Constructor<?>[] availableConstructors = c.getConstructors();
 
//Checking for the loaded constructors
if(availableConstructors != null && availableConstructors.length > 0){
    System.out.println("***Constructor information***");
    
    //Iterating through each of the constructors
    for(Constructor<?> ctr : availableConstructors){
        //Accessing each of the constructors
        try {
            System.out.println("Initiating the object...");
            
            //Load the parameters for the constructors
            Class<?>[] parameters = ctr.getParameterTypes();
            
            //Based on these parameters you can invoke respective constructors
            //newInstance(Object[] params) method takes parameters for the 
            //creating the instance and should be in the same order they appear

            //The below method is used in order to invoke default constructor
            ci = ctr.newInstance(new Object[0]);
            
        } catch (InstantiationException e) {
            // Case in which the instance cannot be created
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // Case in which the constructor is not accessible
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            // Case in which the constructor with specified args is not defined
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            // Case in which the constructor can not be invoked because not defined
            e.printStackTrace();
        }
    }
}

Accessing the Fields

We can access the fields of the loaded class. The getFields() of Class returns an array of Field which is public. We can also access the private fields of the class instance but in that case we need to know the name of the field in prior. We will see that too in the code snippet.

Java
//3. Accessing the fields
//Loading the fields of the class loaded using the reflection
Field[] fields = c.getFields();
 
//Iterating through the fields if any of the public fields found            
if(fields != null && fields.length > 0){
    System.out.println("***Printing the fields***");
    for(Field f : fields){
        System.out.println(f.getName());
        
        //The following method returns the language modifier which is encoded in
        //form of integer
        int modifierInformation = f.getModifiers();
        
        //Thus you can also get the modifier of the field and perform operations 
        //accordingly(You can think of creating an object explorer and displaying
        //different icons for different modifiers in the explorer in-front of the
        //name of the fields).
        System.out.println("The modification code is: "+modifierInformation);
    }
}
else{
    System.out.println("***There are no public fields****");
                
    System.out.println("Trying to access the private field...");
    try {
        //Loading a specific field
        Field privateField = c.getDeclaredField("categoryId");
        
        //Setting the accessibility of the field true in case of accessing the 
        //private field it is compulsory
        privateField.setAccessible(true);
        System.out.println("Got access to: "+privateField.getName());
        
        //Setting the value of the field loaded
        privateField.set(ci, 56);
 
        //Retrieving the value of the field
        System.out.println(privateField.get(ci)); 
    } catch (IllegalArgumentException e) {
        // Thrown in case the value to set is not type compatible
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        // Thrown when tried to access even if accessibility is not granted
        e.printStackTrace();
    } catch (NoSuchFieldException e) {
        // Thrown in case the field to be loaded do not exists
        e.printStackTrace();
    } catch (SecurityException e) {
        // Thrown in case of the security manager do not allow some action
        e.printStackTrace();
    }
                
}

Accessing the Methods

We can access the public methods of the class from the method getMethods(). This method returns all the methods of the classes which are public. The above method will return an array of Method. It will return an array of zero length in case there are no public methods or in case of interface. For invoking a method from the reflection, we need to provide an instance of the class and also the parameters of the method required as Object[]. If we know the above mentioned parameters in advance for the invoke method, we can invoke the function and also get the return value of the function if any.

Java
Method[] methods = c.getMethods();
            
if(methods != null && methods.length > 0){
     System.out.println("\t-> " + 
pt.getCanonicalName());for(Method m : methods){    
System.out.println("---Method information---");
    System.out.println("Method Name: "+m.getName());
    System.out.println("Method's return type: "+m.getReturnType().toString());
    Class<? extends Object>[] paramTypes = m.getParameterTypes();
    Object[] params = new Object[0];
    if(paramTypes != null && paramTypes.length > 0){
        params = new Object[paramTypes.length];
        System.out.println("Parameters of the methods =>");
        int paramIndex = 0;
        for(Class<? extends Object> pt : paramTypes){
            //We can generate the parameter base on the parameter types.
            //You need to write the similar method which can return the 
            //appropriate parameter, which can be used later to invoke the
            //methods.
            params[paramIndex++] = generateParameter(pt);
        }
    }
    //Invoking the method
    try {
        //ci is the instance of the loaded class and the constructor can be
        //invoked using the point 2
        m.invoke(ci, params);
    } catch (IllegalAccessException e) {
        // Thrown in case the method is not accessible
        e.printStackTrace();
    } catch (IllegalArgumentException e) {
        // Thrown in case the method is passed invalid list of arguments
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        //Thrown in case the ci passed is not valid in for the invocation of the
        //method loaded.
        e.printStackTrace();
    }
}

Accessing the Annotations

Annotation of any class or method or field can be loaded and appropriate actions can be taken. Its importance can be appreciated in case you want to make a field which is not serializable. Can you do it with the use of annotation? The annotations can be accessed as mentioned below:

Java
//5. Accessing the annotations
//Loading the annotation information
Annotation[] annotations = c.getAnnotations();
 
//If any of the annotations exists start accessing
if(annotations != null && annotations.length > 0){
    for(Annotation a: annotations){
        System.out.println("--------------Annotation information-------------");
        System.out.println("Annotation name: " + a.toString());
        System.out.println("------------------------------");
    }
} 

Points of Interest

From the above tip, we can understand how the dependency injection or object explorer or some discovery logic works. We can think of intellisense working for some IDE. Though the intellisense does not work in the same way, we can get a feeling from it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)