next up previous contents
Next: Concurrent programming Up: Reflection Previous: Reflection   Contents


Reflection

A programming language is said to be reflective9.1 if it can represent (part of its) state as data and manipulate it. There are two components involved in reflection:

Constructions such as the following

   Employee e = new Manager(...);
   ...
   if (e instanceof Manager){
      ...
   }

are very simple examples of reflection at work. We can, at run time, check the actual type of an object.

However, notice that in this case we know the type that we want to check in advance--both Employee and Manager are already defined when this code is written. This is important because the second argument to instanceof must be a fixed name for a class, not a ``class variable''.

What if we wanted to check whether two pointers are the same at run-time? We would like to write a function like:

  public static boolean classequal(Object o1, Object o2){
    ...
    // return true iff o1 and o2 point to objects of same type
    ...
  }

To implement this using instanceof, we would have to check this for every possible class that we know whether o1 and o2 are both instances of the same class. This is infeasible for two reasons:

It turns out that Java allows us to extract the class of an object at runtime, using the method getClass(), defined in the Java reflection package java.lang.reflect. Thus, we write the following code to implement the classequal we were looking at earlier.

import java.lang.reflect.*;

class MyReflectionClass{
    ...
    public static boolean classequal(Object o1, Object o2){
        return (o1.getClass() == o2.getClass());
    }
}

What values are being compared in the test o1.getClass() == o2.getClass()? In other words, what does getClass() return? For the purpose of classequal, it would be sufficient to have a naïve return value, such as a String containing the name of the the class. In fact, getClass() does much more. It returns a representation of the class of the object on which it is invoked. This means that there is a built in class in Java that can store such a representation. This built in type is called, somewhat confusingly, Class! To make this explicit, we can rewrite classequal as follows.

import java.lang.reflect.*;

class MyReflectionClass{
    ...
    public static boolean classequal(Object o1, Object o2){
        Class c1, c2;
        c1 = o1.getClass();
        c2 = o2.getClass();
        return (c1 == c2);
    }
}

Notice that we are representing dynamic information about the state of the system (the runtime class of o1 and o2) in terms of data c1 and c2 that can be manipulated by the program. This process of encoding execution state as data is called reification.

Having got a representation for a class in a Class object, we can do various things with it. For instance, we can create new instances of this class.

   ...
   Class c = obj.getClass();
   Object o = c.newInstance();  // Create a new object of same type as obj
   ...

Another way of obtaining a class object is to supply the name of the class as a string to the static function forName defined in Class.

   ...
   String s = "Manager".
   Class c = Class.forName(s);
   Object o = c.newInstance();
   ...

We could, of course, simplify this into a single statement as follows:

   ...
   Object o = Class.forName("Manager").newInstance();
   ...

Once we have a class stored in an object of type Class, we can find out details about its constructors, methods and fields (instance variables). A constructor, method or field is itself a complex quantity. For instance, to full describe a constructor we need to provide the types of arguments. For a method we need the arguments and the return type. For all three, we need the modifiers (static, private etc). Thus, it makes sense to have additional classes called Constructor, Method and Field to store information about an individual constructor, method or field. Given an object c of type Class, we can invoke the functions getConstructors(), getMethods() and getFields() to obtain the list of constructors, methods and fields of c. Each of these functions returns an array. So, for instance, we could write

   ...
   Class c = obj.getClass();
   Constructor[] constructors = c.getConstructors();
   Method[] methods = c.getMethods();
   Field[] fields = c.getFields();
   ...

Not surprisingly, we can now operate on each of Constructor, Method and Field to get further details of these objects. For instance, the list of arguments to a constructor or a method is a list of types, or, in other words, an array of Class[]. Thus, we can write

   ...
   Class c = obj.getClass();
   Constructor[] constructors = c.getConstructors();
   for (int i = 0; i < constructors.length; i++){
     Class params[] = constructors[i].getParameterTypes();
     ..
   }

We can also invoke methods and examine/set values of fields.

   ...
   Class c = obj.getClass();
   ..
   Method[] methods = c.getMethods();
   Object[] args = { ... }       // construct an array  of arguments
   methods[3].invoke(obj,args);  // invoke method stored in methods[3]
                                 //  on obj with arguments args
   ...
   Field[] fields = c.getFields();
   Object o =  fields[2].get(obj);  // get the value of fields[2]
                                    // from obj
   ...
   fields[3].set(obj,value);        // set the value of fields[3]
                                    // in obj to value
   ...

Should it be possible to find out about private constructors, methods and fields of a class? The functions getConstructors(), ... only return publicly defined values. However, Java does permit us to access private components using variants of these functions called getDeclaredConstructors(); getDeclaredMethods() and getDeclaredFields(). Of course, there is an issue of security involved here. In general, Java allows us to find out about private methods or fields in a class, but the default security level will not allow us to invoke private methods or access/modify private fields.

The reflective capabilities of Java allow us to write versatile code, such as the Java programming environment BlueJ9.2 that provides an interactive interface to define Java classes, create objects, invoke methods on these objects, examine the state of these objects etc. The important fact is that BlueJ does not have to implement a nested virtual machine to act as a Java ``interpreter''. Using the reflective capabilities of Java, BlueJ can directly perform its manipulations using the JVM within which it is itself running!

Of course, one might ask for even more--there is no way to directly create or modify new classes by writing something like

   Class c = new Class(....);

Creating class objects is done indirectly by the class loader, which is outside the scope of this discussion. Nevertheless, Java does provide a rather powerful set of tools for reflective programming.


next up previous contents
Next: Concurrent programming Up: Reflection Previous: Reflection   Contents
Madhavan Mukund 2004-04-29