Java Reflection is the process of analyzing and modifying all the capabilities of class at runtime. Reflection API is used to manipulate class & its fields, methods, and constructor at run-time.
The java.lang.reflect package provides many classes to implement reflection. Methods of java.lang.Class is used for extracting the complete metadata of a class.
Classes in java.lang.reflect package:
As per the Oracle’s document, following is the list of various java classes in java.lang.reflect package to implement reflection (ref:https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/package-summary.html):
- AccessibleObject:
The AccessibleObject class is the base class for Field, Method and Constructor objects. It provides the ability to flag a reflected object as suppressing default Java language access control checks when it is used. The access checks for public, default (package) access, protected, and private members are performed when Fields, Methods or Constructors are used to set or get fields, to invoke methods, or to create and initialize new instances of classes, respectively.Setting the accessible flag in a reflected object permits sophisticated applications with sufficient privileges, such as Java Object Serialization or other persistence mechanisms, to manipulate objects in a manner that would normally be prohibited.
By default, a reflected object is not accessible.
- Array:
The Array class provides static methods to dynamically create and access Java arrays. - Constructor<T>:
This class provides information about access modifier, name and parameter type of a constructor. - Executable (Java 8):
A shared superclass for the common functionality of Method and Constructor. - Field:
This class provides information about the datatype, access modifier, name and value of a variable. - Method:
This class provides information about access modifier, return type, name, parameter types and exception type of a method. - Modifier:
This class provides information about a particular access modifier. - Parameter (Java 8):
This class provides information about method parameters, their name and modifier. It also provides an alternate means of obtaining attributes for the parameter. - Proxy:
This class provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods. - ReflectPermission:
This class is a permission class of reflective operations.
Method used from java.lang.Class for reflection:
- Public String getName(): Provides the name of the class.
- Public Class getSuperclass(): Provides the super class reference.
- Public Class[] getInterfaces(): Provides array of interfaces implemented by the class
- Public in getModifiers(): Provides integer value representing the modifiers of the class which needs to be passed as parameter to “public status String toString(int i)” method to get the access specifier for the class.
How to get the metadata?
Get class object:
//Create Class Object by - By using Class.forName() method
Class classForName = Class.forName("com.manishsanger.Vehicle");
//Create Class Object by - By using .class
Class&Vehicle> vehicleClass = Vehicle.class;
//Get Class object from a object
Class vehicle = new Vehicle("SUV", 2200).getClass();
System.out.println("Class name form Vehicle object: " + vehicle.getName());
Get class metadata:
System.out.println("Class name form Vehicle object: " + vehicle.getName());
//Get the name of Super class
System.out.println("Name of super class: " + vehicle.getSuperclass().getName());
//Get the list of implemented interfaces
System.out.println("List of implemented interfaces:");
for(Class vehicleInterface : vehicle.getInterfaces()){
//Print the name of implemented interfaces
System.out.println(vehicleInterface.getName());
}
//Get access modifier using get modifiers and toString method of java.lang.reflect.Modifier
int modifier = vehicle.getModifiers();
System.out.println("\n\nAccess modifier of the class is: \nNumber is:" +modifier+", Name is:"+ Modifier.toString(modifier));
/* How to get the Meta data of the variables*/
System.out.println("\n\n/* How to get the Meta data of the variables? */");
for(Field field : vehicle.getDeclaredFields()){
System.out.println("Class parameter Name:"+field.getName() +",type is:" + field.getType() + field);
}
Get class constructor metadata:
for(Constructor constructor : vehicle.getConstructors()){
System.out.println("\n\nName of constructor: " + constructor.getName());
System.out.println("Constructor modifier: " + Modifier.toString(constructor.getModifiers()));
for(Parameter constructorParameter : constructor.getParameters()){
System.out.println("Constructor parameters name: " + constructorParameter.getName());
System.out.println("Constructor parameters type: " + constructorParameter.getType());
System.out.println("/* How to get the exception thrown by constructor? */");
for(Class constructorExceptionClass : constructor.getExceptionTypes()){
System.out.println("Constructor exceptions: " + constructorExceptionClass.getName());
}
}
}
Get method metadata:
for(Method method : vehicle.getDeclaredMethods()){
System.out.println("Method Name: " + method.getName());
System.out.println("Return Type: " + method.getReturnType());
System.out.println("Default value: " + method.getDefaultValue());
System.out.println("Get the name of declaring class: " + method.getDeclaringClass().getName());
int methodModifiers = method.getModifiers();
System.out.println("Method Access modifiers:" + Modifier.toString(methodModifiers));
System.out.println("\n\n/* How to get the exception thrown by method? */");
for(Class exceptionClass : method.getExceptionTypes()){
System.out.println("Method exceptions: " + exceptionClass.getName());
}
}
Reflection on runtime:
Inspect constructor of class and instantiate objects at runtime:
Class<Vehicle> vehicleClass = Vehicle.class;
Constructor constructor = vehicleClass.getConstructor(String.class, Integer.class);
Vehicle vehicle = (Vehicle) constructor.newInstance("SUV", 2200);
Invoke method as runtime:
// Creates object of desired method by providing the method name as argument to the getDeclaredMethod
Method setTypeMethod = vehicleClass.getDeclaredMethod("setType", String.class);
Method setDisplacementMethod = vehicleClass.getDeclaredMethod("setDisplacement", Integer.class);
// invoke the method at runtime
setTypeMethod.invoke(vehicle, "SUV");
setDisplacementMethod.invoke(vehicle, 2400);
Update variable at runtime:
// creates object of the desired field
Field type = vehicleClass.getDeclaredField("type");
// Allows the object to access the field irrespective of the access specifier used with the field
// By default, a reflected object is not accessible.
type.setAccessible(true);
//takes object, and assign new value to field
type.set(vehicle, "Sedan");
// creates object of the desired field
Field displacement = vehicleClass.getDeclaredField("displacement");
// Allows the object to access the field irrespective of the access specifier used with the field
//By default, a reflected object is not accessible.
displacement.setAccessible(true);
//takes object, and assign new value to field
displacement.set(vehicle, 1600);
System.out.println(vehicle.toString());
Advantages of Using Reflection:
- An application can use external, user-defined classes by creating instances of extensibility objects using their fully-qualified names.
- Debuggers use reflection to examine private members on classes.
Disadvantages of using reflection:
- Performance: As reflection resolve types dynamically, it involves scanning of classpath to load the class which causes performance impact.
- Maintenance: Reflective code is difficult to understand and debug because of runtime class loading makes it difficult to maintain.
- Security Restrictions: Reflection requires runtime permissions which might not be available for system running under security manager.
- Security: Using reflection we can access code which we are not suppose to access. This can be a serious security threat.