dcsimg
December 2, 2016
Hot Topics:

Explore Core Features of Reflection and Its Utility in Java

  • May 22, 2015
  • By Manoj Debnath
  • Send Email »
  • More Articles »

Reflection is not very often used in day to day programming. We use it tacitly in some form or another, though. It is critical especially when we are creating a software tool such as an IDE that requires introspection to enhance productivity and usability of the tool. However, an extensive use of this feature has its drawbacks as well. Reflection can compromise performance of the application in leveraging productivity. A balance between the two is what that matters. The article delves into some of its core areas of the API with a few examples to implement them in programming.

Implicit Use of Reflection

Although not limited to this alone, one of reflection's implicit uses we find in Java Serialization. The Java Serialization framework provides the capability of taking an object and creating a byte representation that can be used to restore the object at a later point in time. This serialization framework uses the reflection mechanism to serialize objects. Programmers must be cautious about heavy use of serialization/deserialization in an application. Overuse can bog down performance in the long run. A similar situation arises when we serialize Java objects into XML so that it becomes available as a cross language-compatible object. We generally achieve this with the help of XMLEncoder and XMLDecoder APIs. These APIs too use reflection to determine which fields are to be encoded. In contrast to object serialization, here, instead of writing the fields into binary, they are written as XML. But, the trap of reflection here is not in encoding objects into XML but to follow the Java Beans Specification. To adhere to the Java bean specification, it has to follow certain norms such as some must-haves like a no-argument constructor, public getter/setter methods, and so forth. Going further into the arena is out of the scope of this article. Interested readers may find standard materials comparing reflection and serialization in Java to get a deeper understanding of the concept.

What We Can Do with Reflection

Perhaps if we know what exactly we can do with reflection, it will will give an idea where to use it judiciously.

  1. From an object reference, we can find out the name of the class of which it is an instance.
  2. Gives a complete description of a class, such as access modifiers or the package it belongs to.
  3. We can know the details of the methods defined in the class, parameter it takes, return type, and access modifier.
  4. Give a full description of the fields defined in the class.
  5. If we have a class name, we can create an object of the class using one of its constructors.
  6. We can get/set the state of an object dynamically.
  7. Dynamic array creation and its element manipulation.

Basis of the Reflection API

The generic class java.lang.Class is the basis of reflection in Java. Because it has no public constructor, objects cannot be instantiated directly. In fact, objects are instantiated at runtime by the Java Virtual Machine (JVM). The objects referred by the Class are specified by a qualified name passed within the angular braces. On instantiation, JVM loads the class's bytecode and creates an instance of the Class class. Each Class object has a separate identity, uniquely identified by a fully qualified name and its classloader. For example:

Class<Employee> empClass = Employee.class;

The classloader creates a single instance of a class unless it is dissimilar. This gives the reason why; the following code shows a compile-time error:

Employee emp = new Employee();
//compile-time error
Class<Employee> empClass = emp.class;

The class literal must be a class name, rather than an object reference. Because every class implicitly extends Object, the getClass() method can be used to instantiate the Class object, as follows:

Employee emp = new Employee();
Class<Employee> empClass =
   (Class<Employee>) emp.getClass();

Or,

Employee emp = new Employee();
Class<? extends Employee> empClass =
   emp.getClass();

We also can use the static forName() method of the Class to instantiate, as follows:

public static Class <?> forName(String className)
   throws ClassNotFoundException

public static Class<?> forName(String name,
   boolean initialize, ClassLoader loader)
   throws ClassNotFoundException

public class IntrospectEmployee {
   public static void main(String[] args)
         throws Exception{
      // ...
      Class<Employee> empClass = (Class<Employee>)
      Class.forName("org.reflection.demo.Employee");

      // ... or

      Class<Employee> empClass=(Class<Employee>)
      Class.forName("org.reflection.demo.Employee", false,
         IntrospectEmployee.class.getClassLoader());
   }
}

Introspecting Classes

Let's create a class and demonstrate how to get a description of its name, access modifiers, and the like. The dummy class we shall use for this purpose is as follows:

package org.reflection.demo;

import java.io.Serializable;
import java.util.Date;

public class Employee implements Serializable,
      Cloneable{
   private static final long serialVersionUID = 1L;
   private String name;
   private Date joinDate;

   public Employee() {
      super();
   }

   public Employee(String name, Date joinDate) {
      super();
      this.name = name;
      this.joinDate = joinDate;
   }

   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }

   public Date getJoinDate() {
      return joinDate;
   }

   public void setJoinDate(Date joinDate) {
      this.joinDate = joinDate;
   }
}

Listing 1: Dummy class

package org.reflection.demo;

import java.lang.reflect.Modifier;
import java.lang.reflect.TypeVariable;

public class IntrospectEmployeeClass {

   public static void main(String[] args) {
      Class<Employee> e = Employee.class;
      showClassInfo(e);
   }

   public static void showClassInfo(Class<?> c) {
      String str="";
      int mod=0;
      if (c.isPrimitive()) {

      } else if (c.isInterface()) {
         mod=c.getModifiers()& Modifier.interfaceModifiers();
         if (c.isAnnotation()) {
            str+=" @interface";
         } else {
            str+=" interface";
         }
      } else if (c.isEnum()) {
         mod=c.getModifiers()& Modifier.classModifiers();
         str+=" enum";
      } else {
         mod=c.getModifiers()& Modifier.classModifiers();
      }

      String finalStr=Modifier.toString(mod)+"
         "+str+c.getSimpleName()+getGenericTypeParams(c);
      if(c.getSuperclass() != null)
         finalStr+=" extends "+c.getSuperclass().getSimpleName();

      if (getClassInterfaces(c) != null)
         finalStr+=" implements "+getClassInterfaces(c);

      System.out.println(finalStr);
   }

   public static String getClassInterfaces(Class<?> c) {
      Class<?>[] interfaces = c.getInterfaces();
      String list = null;
      if (interfaces.length > 0) {
         String[] namesOfInterfaces =
            new String[interfaces.length];
         for (int i = 0; i < interfaces.length; i++) {
            namesOfInterfaces[i] =
               interfaces[i].getSimpleName();
         }
         list = String.join(", ", namesOfInterfaces);
      }
      return list;
   }

   public static String getGenericTypeParams(Class<?> c) {
      String str = "";
      TypeVariable<?>[] typeParameters =
         c.getTypeParameters();
      if (typeParameters.length > 0) {
         String[] paramNames =
            new String[typeParameters.length];
         for (int i = 0; i < typeParameters.length; i++) {
            paramNames[i] = typeParameters[i].getTypeName();
         }
         str += "<";
         String parmsList = String.join(",", paramNames);
         str += parmsList;
         str += ">";
      }
      return str;
   }

}

Listing 2: Detail information about the Employee class using reflection

Introspecting Fields, Methods, and Constructors

In a similar manner, we can introspect on fields, methods, and constructors declared in a class, as follows:

package org.reflection.demo;

import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;

public class IntrospectFieldMethodConstructor {

   public static void main(String[] args) {
      Class<Employee> c = Employee.class;
      showFieldInfo(c);
      showMethodInfo(c);
      showConstructorInfo(c);
   }

   public static void showFieldInfo(Class<?> c) {
      Field[] fields = c.getDeclaredFields();
      for (Field f : fields) {
         System.out.println(Modifier.toString(f.getModifiers()
            & Modifier.fieldModifiers())
            + " " + f.getType().getSimpleName()
            + " " + f.getName());
      }
   }

   public static void showMethodInfo(Class<?> c) {
      Method[] methods = c.getDeclaredMethods();

      for (Method m : methods) {
         Parameter[] params = m.getParameters();
         String str = "";
         for (int i = 0; i < params.length; i++) {
            str += Modifier.toString(params[i].getModifiers()
               & Modifier.parameterModifiers())
               + " ";
            str += params[i].getType().getSimpleName() + " ";
            str += params[i].getName();
         }
         System.out.println(Modifier.toString(m.getModifiers()
            & Modifier.methodModifiers())
            + " "
            + m.getReturnType().getSimpleName()
            + " "
            + m.getName() + " " + str);
      }
   }

   public staticvoid showConstructorInfo(Class<?> c) {
      Constructor<?>[] constructors =
         c.getDeclaredConstructors();

      for (Constructor<?> con : constructors) {
         Parameter[] params = con.getParameters();
         String str = "";

         for (int i = 0; i < params.length; i++) {
            str += Modifier.toString(params[i].getModifiers()
               & Modifier.parameterModifiers())
               + " ";
            str += params[i].getType().getSimpleName() + " ";
            str += params[i].getName();

         }
         System.out.println(Modifier.toString(con.getModifiers()
            & Modifier.methodModifiers())
            + " " + con.getName() + " " + str);
      }
   }

}

Listing 3: Retrieving information about the fields, methods, and constructor defined in the Employee class using reflection

Creating Dynamic Objects

Java also lets you create object without any need to know the name of a class until runtime. For example, if we have a reference of a Class object, we can create an object of the class using the newInstance() method, as follows:

Class <Employee> c = Employee.class;
Employee emp = c.newInstance();

The return type of the method newInstance() is derived from the type parameter defined within the angular braces of Class<T>. The preceding code is similar to writing:

Employee emp = new Employee();

Observe that the newInstance() method does not take any parameter. Now, we want to create an object using a specific parameterized constructor of the Employee class. How are we going to do that? Java reflection has a provision for that, too. In such a situation we can write it as follows:

Constructor<Employee>
   empCon=c.getConstructor(String.class, Date.class);

And then we can write:

Employee emp = empCon.newInstance(“Harris”, new Date());

It's quite interesting to see how objects can be created at runtime. These ideas are the building block of so many frameworks Java such as Spring, Hibernate, and the like.

Conclusion

These are some of the rudimentary sketches of the reflection feature in Java. As you dive deeper, you'll feel the power of the core APIs and how extensively it can be used to leverage productivity. However, be cautious of unnecessary use of reflection in an application because it can make your application performance slow.


Tags: Java, XML, API, class, Reflection, Java Serialization, serialize, deserialize, constructor




Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date
Rocket Fuel