Have you never tried java reflections? Then, try it now.
Reflections allow you access fields and methods of java
classes even though they are not publicly visible. So, if you want to develop a
new debugger or a testing framework to test your codes, reflections is your
friend.
OK, let’s start simple. First of all, let’s define a class,
so we can perform our operations on that class.
class Person { public String name = "John Smith"; public int age = 42; protected long[] contactNumbers = new long[]{new Long("94771234567"), new Long("94717654321")}; private String nid = "867893334V"; public void sayName() { System.out.println(name); } public String getNID(String authenticationId) throws InvalidActivityException { if ("123".equals(authenticationId)) { return nid; } else { throw new InvalidActivityException("Not Allowed!"); } } }
Class Types and Instances
Ok, now we want to create an instance from this class. Yes
we can simply get an instance by,
Person person = new Person();
But let’s do it in ‘reflections way’. First we need to know
the class type. The easiest way to get the class type is,
Class cls = Person.class;Assume for some requirements, you only have the name of the class; still we can get the class type,
String className = "com.blogspot.hiruss.reflections.Person"; Class cls = Class.forName(className);
However, this will throw ‘ClassNotFoundException’ if you
provide a wrong class name. Now we can create a new ‘Person’ instance, using
this class type,
Object newInstance = cls.newInstance();If you want, you can cast it in to Person object.
Observing and Retrieving Field Variables
Let’s have a look in to the
field variables first. Check out the comments for more details.
//This method will return all the field varibales as an array Field[] declaredFields = cls.getDeclaredFields(); for (Field field : declaredFields) { //Returns the name of the field System.out.println("Field: " + field.getName()); //Returns the type of the filed Class type = field.getType(); System.out.println("\tType: " + type.getName()); //Returns the modifier of the field as an integer, we can decode //it using 'Modifier' class. int modifiers = field.getModifiers(); System.out.println("\tModifiers: " + Modifier.toString(modifiers)); //If the modifier is not 'private', we can get the value of the //field directly. For thet, we have to call 'get' on the field with //the instance of Person that we wish to get the value. //Even though they are private feilds, we can get their values by //setting accessibility ;). Otherwise it will throw an 'IllegalAccessException' boolean isPrivate = Modifier.isPrivate(modifiers); if (isPrivate) { field.setAccessible(true); } System.out.println("\tValue: " + field.get(newInstance)); }The output for the above code would be,
Field: name Type: java.lang.String Modifiers: public Value: John Smith Field: age Type: int Modifiers: public Value: 42 Field: contactNumbers Type: [J Modifiers: protected Value: [J@574f7121 Field: nid Type: java.lang.String Modifiers: private Value: 867893334VIf you check the above output, you will see the type of the ‘contactNumbers’ field is ‘[j’. This means it is an array of long. Let’s see more details of that field.
//returns the field directly by the name Field contactNumbersField = cls.getDeclaredField("contactNumbers"); //the type of the field. Class type = contactNumbersField.getType(); System.out.println("\tArray type: " + type.getName()); //checks whether its an array or not System.out.println("\tIs array: " + type.isArray()); //If it's an array, we can check the type of it's components Class componentType = type.getComponentType(); System.out.println("\tArray's component type: " + componentType.getName()); //return the array as a object and cast it to a long array, and prints the values long[] contactNumbers = (long[]) contactNumbersField.get(newInstance); for (long number : contactNumbers) { System.out.println("\t" + number); }The output is,
Array type: [J Is array: true Array's component type: long 94771234567 94717654321
Observing and Invoking Methods
Now, about methods,
//returns all the methods as an array Method[] declaredMethods = cls.getDeclaredMethods(); for (Method method : declaredMethods) { //returns the name of the method String name = method.getName(); //returns the return type of the method Class returnType = method.getReturnType(); //returns the exception types Class[] exceptionTypes = method.getExceptionTypes(); //returns the types of the method parameters Class[] parameterTypes = method.getParameterTypes(); System.out.println("Method Name: " + name); System.out.println("\tReturn Type: " + returnType.getName()); System.out.println("\tExceptions: "); for (Class c : exceptionTypes) { System.out.println("\t\t" + c.getName()); } System.out.println("\tParameters: "); for (Class c : parameterTypes) { System.out.println("\t\t" + c.getName()); } }The output,
Method Name: sayName Return Type: void Exceptions: Parameters: Method Name: getNID Return Type: java.lang.String Exceptions: javax.activity.InvalidActivityException Parameters: java.lang.String
Let's try to use one of those methods,
//returns the method directly by name //the second argument is an array of types of the parameters. Since //'sayName' method has no parameters, I'm passing an empty array. You //can pass null if you want. Method sayNameMethod = cls.getDeclaredMethod("sayName", new Class[0]); //invoke the method on the given instance of the person class. The second //argument is the parameter values for the method. Since this method has //no parameters, I'm just passing an empty object array. Same as above, you //can pass null here. sayNameMethod.invoke(newInstance, new Object[0]);The output would be 'John Smith'.
Try it now, You can download the complete code SimpleReflections.java. Make sure that you put this java file in the correct package (com.blogspot.hiruss.reflections) or to change the 'className' variable according to your package.