Reflection in Java

All Java classes are subclasses of a pre-defined base class called Object:

public class Object {
   protected Object clone() { ... }
   public boolean equals(Object obj) { ... }
   public String toString() { ... }
   public void notify() { ... }
   public void wait() { ... }
   public int hashCode() { ... }
   public Class getClass() { ... }
   // etc.
}

The last method is interesting. What sort of answer do we expect when we ask an object what class it instantiates? We expect a class, of course. But only objects, not classes, exist at runtime. How can an object return a class in response to this query? The solution is that instead of returning a class, it returns an object representing a class. At first this notion sounds strange. How can objects represent classes? But of course objects represent all sorts of things: cars, houses, boats, people, cats, planets. Why not classes?

The class of all objects that represent classes is defined in the java.lang package. Naturally, the name of this class is Class. It includes methods for discovering the name of the class (getName), the super class (getSuperClass), the methods (getMethods), and the attributes (getFields):

class Class<T> {
   public static Class forName(String name) { ... }
   public Object newInstance() { ... }
   public String getName() { ... }
   public Class getSuperClass() { ... }
   public Field getField(String name) { ... }
   public Field[] getFields() { ... }
   public Method getMethod(String name) { ... }
   public Method[] getMethods() { ... }
   // etc.
}

Of course this implies that there are also objects representing methods and fields. Naturally, these objects belong to the Method and Field classes, respectively, and of course like all Java classes, Class, Method, and Field are subclasses of the Object base class:

Example

For our example we create Note.java, which contains a simple class hierarchy:

Notes

·        HornNote and ViolinNote overrieNote.play.

·        Note.frequency and Note.duration are public.

·        Demonstrations are in ReflectionDemos.java.

Runtime Type Information (RTTI) in Java

Recall that Java objects are polymorphic. That means they can have many types/classes, starting with the class it instantiates and including all classes and interfaces that class inherits from. For example, an instance of HornNote has three types: HornNote, Note, and Object.

The following method displays all types of an object by climbing up its class hierarchy:

   public static void getTypes(Object object) {
      Class<?> c = object.getClass();
   System.out.println("object types:");
      while(c != null) {
      System.out.println("\t" + c.getName());
         c = c.getSuperclass();
      }
   }

Here's the output produced by getTypes(new ViolinNote()):

object types:
reflectionDemos.ViolinNote
reflectionDemos.Note
   java.lang.Object

Introspection in Java

In addition to information about super classes, we can also find out about the methods and fields of a class. Assuming c still references an object representing the ViolinNote class, then the following loop prints out the names of all of the ViolinClass methods:

Method methods[] = c.getMethods();
for(int i = 0; i < methods.length; i++)
System.out.println(methods[i].getName());

Here's the output produced:

hashCode
wait
wait
wait
getClass
equals
toString
notify
notifyAll
play

Notice that in addition to play(), all methods inherited from the Note and Object super classes are also listed. (The wait() method inherited from the Object super class appears three times because there are actually three different methods with this name.) Of course we could have printed out much more than the names of the methods. For example, we could have printed the parameter lists, the exception lists, and the return types.

The following code prints the names of the ViolinNote fields as well as their current values in the particular ViolinNote object referenced by note:

Field fields[] = c.getFields();
try {
   for(int i = 0; i < fields.length; i++) {
   System.out.print(fields[i].getName() + " = ");
   System.out.println(fields[i].getInt(note));
   }
} catch(Exception e) {
      // handle e
}

Here is the output produced:

frequency = 60
duration = 300

Non-public fields aren't printed.

Setting Fields

Public fields can also be set using reflection:

    public static void setFields() {
        try {
            Note note = new Note();
            System.out.println(note);
            Class<Note> c = Note.class; // an easier way to get a class object
            Field f = c.getDeclaredField("frequency");
            f.setInt(note, 2 * f.getInt(note));
            f =  c.getDeclaredField("duration");
            f.set(note, 2 * (Integer)(f.get(note)));
            System.out.println("... and now " + note);
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }
    }

Here's the output produced:

Note: frequency = 60 and duration = 300
... and now Note: frequency = 120 and duration = 600

Invoking Method Objects

Surprisingly, we can ask a Method object to invoke the method it represents. Of course we must provide it with the implicit and explicit arguments. For example, let's create a generic Note object, then call its play() method using reflection:

note = new Note();
c = note.getClass();
Method meth = c.getMethod("play", null);
meth.invoke(note, null);

Here's the output produced:

Playing a generic note

We repeat the experiment using a HornNote:

note = new HornNote();
c = note.getClass();
meth = c.getMethod("play", null);
meth.invoke(note, null);

Here's the output produced:

Playing a horn note

Notice that the HornNote play() method was invoked instead of the Note play() method.

Finally, we repeat the experiment one last time using a ViolinNote:

note = new ViolinNote();
c = note.getClass();
meth = c.getMethod("play", null);
meth.invoke(note, null);

Here's the output produced:

Playing a violin note

Of course it's far more efficient to simply call the play() method directly:

note.play();

Reflection is useful in those situations where we don't know which method we want to call at the time we are writing our program. Instead, this information will only be available at runtime.

Dynamic Instantiation in Java

Sometimes we don't even know the type of object we want to create until our program is running. We saw examples of this problem when we introduced the Factory Method Pattern. In Java, we can create an instance of a class, C, from a Class object representing C using the newInstance() method. To make things harder, we will assume nothing is known about C at compile time. This might be the case if we were trying to define a universal instrument class. A universal instrument can imitate all other types of instruments. This is done with a play() method that expects as its input only the name of the type of note to play:


   public void play(String noteType) {
      try {
         Class<?> c = Class.forName(noteType);  // find & load a class
         Note note = (Note) c.getDeclaredConstructor().newInstance();
         note.play();
      } catch (Exception e) {
         // handle e here
      }
   }

Internally, the play() method first converts the name of the class, noteType, into an object representing the class itself, using the static forName() method. For example, if noteType is the string "HornNote", then forName() searches for a file named HornNote.class (this is the conventional name for the file containing the binary definition of the HornNote class), dynamically loads the file into the Java virtual machine, then creates and returns a Class object representing the HornNote class.

From the Class object, c, the newInstance() method is invoked. This creates an instance of the class represented by c. Of course this is returned as an Object, so in our example we perform an explicit downcast to the Note class, then call the play() method.

After creating a universal instrument, our test driver calls the play() method twice. The first time the string "ViolinNote" is the argument. The second time the string "HornNote" is the argument:

noteType = "ViolinNote";
play(noteType);
noteType = "HornNote";
play(noteType);

Here's the output produced:

Playing a violin note
Playing a horn note

Of course if we wanted to create and play a HornNote followed by a ViolinNote, why not simply do it directly:

note = new HornNote();
note.play();
note = new ViolinNote();
note.play();

To see why, suppose instead of hardwiring the "ViolinNote" and "HornNote" strings into our test program, we allow the user to specify the strings:

System.out.print("enter a type of note: ");
noteType = MyTools.stdin.readLine();
play(noteType);

We don't know what the user will enter, so we don't know what type of notes to make.

Universal Program

Example: A universal application

public class UniversalProgram {
   public void exec(String className, String methName) {
      try {
         Class c = Class.forName(className);  // find & load a class
         Method meth = c.getMethod(methName, null);
         Object blob = c.newInstance();
         meth.invoke(blob, null);
      } catch (Exception e) {
         // handle e here
      }
   }
   // usage:
   public static void main(String[] args) {
      UniversalProgram turing = new UniversalProgram();
      String objectType = args[0];
      turing.exec(objectType, args[1]);
   }
}

Why do we need reflection?

Think about plugins. When we add a third-party plugin to our browser it just starts working. We don't need to rewrite any browser code. How does the browser know what the plugin is supposed to do? The answer is: it asks. It uses the plugin name to load the plugin class. It uses dynamic instantiation to create an instance of the class. It uses introspection to find out what methods the class offers. It uses invocation to call these methods.