Introduction
During your career as a Software Designer/Programmer you will probably encounter the need to use Objects at run-time without knowing much about them at design-time. Java enables this with Reflection.
Java Generic Code - Dealing With Reflection, the Easy Way
If you have ever done any work with JDBC then the following code would seem familiar:
public Connection getConnection(String driverClass, String dbUrl, String userName,
String passWord) {
Connection connection = null;
try {
if (Class.forName(driverClass) != null) {
connection = DriverManager.getConnection(dbURL, userName, passWord);
if (connection != null) {
connection.setAutoCommit(false);
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
return connection;
}
If you ever written similar code, you've used Reflection.
What is it anyway?
Reflection is a set of API's that Java employs to solve a variety of problems. For instance, using an IDE to design a GUI involves playing with a lot of controls, adding them to a frame and tweaking their properties. Your IDE (if written in Java) is using Reflection to load the classes, determine what properties they have, allowing you to modify those properties and invoke the appropriate methods in the objects to change the properties to the values you've chosen.
What can we do with it?
As you know, runtime Objects must be loaded and have fields and methods. Therefore Reflection concentrates on three things:
- Letting you load classes on the fly when you only know the name of the class during run-time.
- Letting you access the fields of an object without actually knowing their names or types beforehand.
- Letting you invoke methods in these anonymous objects.
Why is it hard?
Finding your way around Java's Reflection API can be a challenge, especially when you're pressed for time. For that reason, I have collected most of my knowledge about Reflection into an easy to use utility class. I will present this class in short by using a demo program.
The Demo
The demo program is very simple: I will attempt to load a class by its fully-qualified name (i.e. package name + class name), and then manipulate it by invoking a method known to me by name only.
The class structure for the code in the demo is as follows:
- dev.easyref.tester:
DemoApplication
- The demo's main class.
- dev.easyref.data :
Employee
- A simple data class which I load in run-time.
- dev.easyref.util:
Arguments
- The utility class used to perform all the Reflection work.
The following Employee class simply contains information:
public class Employee {
private int age;
private float salary;
private String firstName;
private String lastName;
public Employee(String firstName, String lastName, int age, float salary) {
this.age = age;
this.salary = salary;
this.firstName = firstName;
this.lastName = lastName;
}
public void updateSalary(float increase) {
salary += increase;
}
public String toString() {
return "<Employee Name: [" + firstName + ", " + lastName + "], Age: [" + age + "], "
"Salary: [" + salary + "]>";
}
}
The demo code manipulates the Employee class as follows:
1: import dev.easyref.util.*;
2: public class DemoApplication {
3: public static void main(String[] args) {
4: 5: Arguments xArgs = new Arguments();
6: 7: xArgs.addElement("Doron");
8: xArgs.addElement("Barak");
9: xArgs.addElement(36);
10: xArgs.addElement(3000.0f);
11: 12: Object o = xArgs.getInstance("dev.easyref.data.Employee");
13: 14: System.out.println("Created instance >> " + o);
15: 16: xArgs = new Arguments(o);
17: 18: System.out.println("Instace data >> " + xArgs);
19: 20: xArgs.setElementAt(1000.0f, 1);
21: 22: xArgs.hideExceptions();
23: 24: xArgs.runMethod(o, "updateSalary");
25: 26: if (xArgs.hadMethodError()) {
27: 28: xArgs.removeElementAt(0);
29: xArgs.removeElementAt(1);
30: xArgs.removeElementAt(1);
31: 32: xArgs.runMethod(o, "updateSalary");
33: }
34: 35: System.out.println("After Salary Raise >> " + o);
36: }
37: }
Lines 5-10:
Starting at lines 5-10, an Arguments
object is created and data is added to it. The data is added in the order it would appear if the constructor of the Employee
class would have been used. My Arguments
object extends Java's Vector
object for ease of use in manipulating its data elements.
Lines 12-14:
Here the Arguments
object is used to get an instance of the dev.easyref.data.Employee
class and then display the result. The getInstance()
method of the Arguments
object does all the work of calling Class.forName()
and negotiating with properly invoking the constructor of the requested Class.
Lines 16-18:
The Arguments
object is now used to collect all the data present in the Employee
instance and then the Arguments
object is displayed. Please note that the data collected into the Arguments
object matches in its element order and type, the data members declared in the Employee
class.
Lines 20-24:
A float value of 1,000 is set into the first slot of the Arguments object using the setElementAt()
method inherited from the Vector
class. Next the Arguments
object is ordered to hide all run-time exceptions and attempt to invoke the updateSalary()
method of the Employee
instance. As you may have guessed, the reason I hide the exceptions is because invoking the method at this stage does result in a NoSuchMethodException
as the data currently contained in the Arguments
object does not match the method-signature of the updateSalary()
method.
Lines 26-35:
An Arguments
object can be queried to see if the last method-invocation resulted in an error. Since we know that it did, we now remove the unwanted data and try to re-invoke the updateSalary()
method, now with the proper value. To prove that the salary has been properly raised, I display the Employee
instance again.
The output of the demo application should look like this:
Created instance >> <Employee Name: [Doron, Barak], Age: [36], Salary: [3000.0]>
Instace data >> [36, 3000.0, Doron, Barak]
ERROR: Method not found for class [dev.easyref.data.Employee] {
updateSalary(int p0, float p1, String p2, String p3);
}
After Salary Raise >> <Employee Name: [Doron, Barak], Age: [36], Salary: [4000.0]>
You probably noticed that even though the Arguments object showed no exception, it did report the unfortunate result of the method invocation with an error message sent to the System.err
stream.
In Conclusion
Download the code and have fun with it. Java's Reflection is a very important tool without which a lot of Java's features simply would not exist. Without Reflection you would not be able to use a sophisticated IDE to design a GUI. You would also not be able to use neither Serialization nor RMI.
You can use the Arguments
class itself in your applications, I only wish I had the time to properly document it.