Saturday, February 16, 2013

A Simple Start to Java Reflections


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: 867893334V
If 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.