Reflection in Java Part 2
In the previous blog, we learned about Reflection in Java. If you want to know about the concept of Reflection visit the blog Reflection in Java . The Reflection mechanism is very powerful. Reflection allows us to runtime inspect objects, runtime analyze capabilities classes, runtime construct generic array manipulation code, and runtime utilize Method objects that function merely like function pointers in languages like C++. In this blog, we will be going through all the above mention concepts in detail.
Using Reflection for Analyzing the Capabilities of Classes in Java
This topic will give us an overview of the important parts of the reflection mechanism. The package java.lang.reflect contains the classes like Field, Constructor, and Method which describe the field, constructor, and method of the class. The classes mentioned above have a method getName() that returns the name of the item. The getType() method of the Field class returns an object of type Class which describes the field type. The Method and Constructor classes contain the methods used for reporting the type of parameter and the Method class also reports the return type. These three classes have the method known as getModifiers which returns the integers with various bits that are turned on and off which describes the modifiers such as public and static. We can use the static methods in the Modifier class present in java.lang.reflect package to analyze the integer that getModifiers returns.
e.g.
e.g.
OR if we give java.lang.String then it will give the following output
e.g.
Using Reflection to Analyze Objects at Runtime
In the previous section, we have seen how we can find out the names and types of the data fields of any object. In this section, we will go one step ahead and actually look at the contents of the fields. We can easily get the contents of a specific field of an object whose name and type are known when we write code. But reflection allows us to look at fields of objects that were not known at compile time. The method which helps in achieving this is the get() method in the Field class. For example, if fieldObj is an object of type Field and obj is an object of the class of which fieldObj is a field, then fieldObj.get(obj) returns an object whose value corresponds to the obj field's current value. Let’s go through the example:
In the above example AtrowelEmployee object describes the properties of the AtrowelEmplyee, same is the case with the Class object which describes the properties of the class. The getName() method of Class is the most commonly used method. This method returns the name of the class.
e.g.
We can also set the values that we can get. The call fieldObj.set(obj, value) sets the field represented by fieldObj of the object obj to the new value. But there is a problem with the above code, i.e. the name field is a private field, the get and set methods will throw an IllegalAccessException. We can use get() and set() only with the accessible fields. The security mechanism of Java allows us to find out what fields an object has but doesn’t allow us to read or write those fields unless we have permission. The default behavior of the reflection is to respect the Java access control. But we can override access control by invoking the setAccessible() method on the Field, Method, and Constructor object.
e.g.
The setAccessible() method is a method of the AccessibleObject class, which is the common superclass of the Field, Method, and Constructor classes. If the access is not granted then the call to setAccessible() throws an exception. Many libraries make use of reflection, because of which Java 9 and 10 only give a warning when we use reflection to access a nonpublic feature inside a module. Now we will look at the generic toString() method which works for any class. To obtain all the data fields the generic toString() method uses getDeclaredFields() and to make all the fields accessible we use setAccessible(). It obtains the name and value for each file. Recursively calling toString() converts each value into a string. The generic toString method requires addressing a couple of complexities. Cycle of references may cause infinite recursion. Hence, the ObjectAnalyzer keeps track of objects that were already visited. toString() can be used to peek inside any object.
e.g.
e.g.
We can also use generic toString() to implement the toString() method of our own class.
e.g.
Invoking Arbitrary Methods and Constructor in Java
We can execute an arbitrary function through a function pointer in C and C++. But Java doesn’t have method pointers. Java Designers say that method pointers are dangerous and error-prone and Java interface and lambda expressions are the superior solution. The reflection mechanism allows us to call arbitrary methods. We have already seen in the previous section that we can inspect the field of an object with the get() method of the Field class. The Method class has an invoke() method that lets us call the method that is wrapped in the current Method object. The syntax for the invoke() method is
In the above statement the first parameter is the implicit parameter and the remaining objects provide the explicit parameters. In the case of a static method the first parameter is ignored i.e. we can set it to null. For example, if method1 represents the getName() method of the AtrowelEmployee class, we can call:
Suppose if the return type is primitive, then the invoke() method will return the wrapper type. For example, if method2 represents the getSalary() method of the AtrowelWmployee class, then the returned object is actually a Double, and we must cast it accordingly. We can use automatic unboxing to turn it into a double. For example
We can obtain the Method object by calling getDeclaredMethods() and search through the returned array of Method objects until we find the method we want or else we can call the getMethod() method of the Class class. It is similar to the getField() method which takes a string with the field name and returns a Field object. It may be possible that there may be multiple methods with the same names, so we must be careful to use the right method. For that reason, we must supply the parameter types of the desired method. The syntax of getMethod() is
e.g.
Similar approach is used for invoking arbitrary constructors. We must provide the constructor’s parameter types to the Class.getConstructor() method, and provide the parameter values to the Constructor.newInstance() method:
e.g.
Output