Reflection

In this section, we’ll take a look at the Java reflection API, supported by the classes in the java.lang.reflect package. As its name suggests, reflection is the ability for a class or object to examine itself. Reflection lets Java code look at an object (more precisely, the class of the object) and determine its structure. Within the limits imposed by the security manager, you can find out what constructors, methods, and fields a class has, as well as their attributes. You can even change the value of fields, dynamically invoke methods, and construct new objects, much as if Java had primitive pointers to variables and methods. And you can do all of this on objects that your code has never even seen before.

We don’t have room here to fully cover the reflection API. As you might expect, the reflect package is complex and rich in details. But reflection has been designed so that you can do a lot with relatively little effort; 20 percent of the effort will give you 80 percent of the fun.

The reflection API is used by JavaBeans to determine the capabilities of objects at runtime. It’s also used at a lower level by object serialization to tear apart and build objects for transport over streams or into persistent storage. Obviously, the power to pick apart objects and see their internals must be zealously guarded by the security manager. Your code is not allowed to do anything with the reflection API that it couldn’t do with static (ordinary, compiled) Java code. In short, reflection is a powerful tool, but it isn’t a loophole. An object can’t use it to find out about data fields that it wouldn’t normally be able to access (for example, another object’s private fields), and you can’t use it to modify any data inappropriately.

The three primary features of a class are its fields (variables), methods, and constructors. For purposes of describing or accessing an object, these three features are represented by separate classes in the reflection API: java.lang.reflect.Field, java.lang.reflect.Method, and java.lang.reflect.Constructor. We can create these objects using the Class object.

The Class class provides two pairs of methods for getting at each type of feature. One pair allows access to a class’s public features (including those inherited from its superclasses), while the other pair allows access to any public or nonpublic item declared within the class (but not features that are inherited), subject to security considerations. Some examples:

  • getFields( ) returns an array of Field objects representing all of a class’s public variables, including those it inherits.

  • getDeclaredFields( ) returns an array representing all the variables declared in the class, regardless of their access modifiers (not including variables the security manager won’t let you see), but not including inherited variables.

  • For constructors, the distinction between “all constructors” and “declared constructors” is meaningful, so getConstructors( ) and getDeclared-Constructors( ) differ only in that the former returns public constructors, while the latter returns all the class’s constructors.

Each pair of methods includes a method for listing all of the items at once (for example, getFields( )) and a method for looking up a particular item by name and—for methods and constructors—by signature (for example, getField( ), which takes the field name as an argument).

The following listing shows the methods in the Class class:

Field [] getFields ( );

Get all public variables, including inherited ones.

Field getField (String name );

Get the specified public variable, which may be inherited.

Field [] getDeclaredFields ( );

Get all public and nonpublic variables declared in this class (not including those inherited from superclasses).

Field getDeclaredField (String name );

Get the specified variable, public or nonpublic, declared in this class (inherited variables not considered).

Method [] getMethods ( );

Get all public methods, including inherited ones.

Method getMethod (String name , Class [] argumentTypes );

Get the specified public method whose arguments match the types listed in argumentTypes. The method may be inherited.

Method [] getDeclaredMethods ( );

Get all public and nonpublic methods declared in this class (not including those inherited from superclasses).

Method getDeclaredMethod (String name , Class [] argumentTypes );

Get the specified method, public or nonpublic, whose arguments match the types listed in argumentTypes, and which is declared in this class (inherited methods not considered).

Constructor [] getConstructors ( );

Get all public constructors of this class.

Constructor getConstructor (Class [] argumentTypes );

Get the specified public constructor of this class whose arguments match the types listed in argumentTypes.

Constructor [] getDeclaredConstructors ( );

Get all public and nonpublic constructors of this class.

Constructor getDeclaredConstructor (Class [] argumentTypes );

Get the specified constructor, public or nonpublic, whose arguments match the types listed in argumentTypes.

As a quick example, we’ll show how easy it is to list all of the public methods of the java.util.Calendar class:

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

Here we have used the .class notation to get a reference to the Class of Calendar. Remember the discussion of the Class class—the reflection methods don’t belong to a particular instance of Calendar itself; they belong to the java.lang.Class object that describes the Calendar class. If we wanted to start from an instance of Calendar (or, say, an unknown object), we could have used the getClass( ) method of the object instead:

Method [] methods = myUnknownObject.getClass().getMethods( );

Security

Access to the reflection API is governed by a security manager. A fully trusted application has access to all of the previously discussed functionality—it can gain access to members of classes at the level of restriction normally granted code within its scope. There is currently no “special” access granted by the reflection API. It is possible that in the future, the full power of the reflection API will be available to completely trusted code; currently, user code can see only what it could have seen at compile time. Untrusted code (for example, an unsigned applet) has the normal level of access to classes loaded from its own origin (classes sharing its class loader), but can rely only on the ability to access the public members of public classes that originate elsewhere.

Accessing Fields

The class java.lang.reflect.Field is used to represent static variables and instance variables. Field has a full set of accessor methods for all of the base types (for example, getInt( ) and setInt( ), getBoolean( ) and setBoolean( )) and get( ) and set( ) methods for accessing members that are object references. For example, consider this class:

class BankAccount { 
    public int balance; 
}

With the reflection API, we can read and modify the value of the public integer field balance:

BankAccount myBankAccount = ...; 
... 
try { 
    Field balanceField = BankAccount.class.getField("balance"); 
    // read it 
    int mybalance = balanceField.getInt( myBankAccount );    
    // change it
    balanceField.setInt( myBankAccount, 42 );
} catch ( NoSuchFieldException e ) {  
    ... // there is no "balance" field in this class
} catch ( IllegalAccessException e2) { 
    ... // we don't have permission to access the field
}

In this example, we are assuming that we already know the structure of a BankAccount object. However the real power of reflection is in examining objects that we’ve never seen before.

The various methods of Field take a reference to the particular object instance that we want to access. In the code shown earlier, the getField( ) method returns a Field object that represents the balance of the BankAccount class; this object doesn’t refer to any specific BankAccount. Therefore, to read or modify any specific BankAccount, we call getInt( ) and setInt( ) with a reference to myBankAccount, which is the particular account we want to work with. An exception occurs if we try to access to a field that doesn’t exist, or if we don’t have the proper permission to read or write to the field. If we make balance a private field, we can still look up the Field object that describes it, but we won’t be able to read or write its value.

Therefore, we aren’t doing anything that we couldn’t have done with static code at compile time; as long as balance is a public member of a class that we can access, we can write code to read and modify its value. What’s important is that we’re accessing balance at runtime, and could use this technique to examine the balance field in a class that was dynamically loaded.

Accessing Methods

The class java.lang.reflect.Method represents a static or instance method. Subject to the normal security rules, a Method object’s invoke( ) method can be used to call the underlying object’s method with specified arguments. Yes, Java has something like a method pointer!

As an example, we’ll write a Java application called Invoke that takes as command-line arguments the name of a Java class and the name of a method to invoke. For simplicity, we’ll assume that the method is static and takes no arguments:

//file: Invoke.java
import java.lang.reflect.*; 

class Invoke { 
  public static void main( String [] args ) { 
    try { 
      Class c = Class.forName( args[0] ); 
      Method m = c.getMethod( args[1], new Class [] { } ); 
      Object ret =  m.invoke( null, null ); 
      System.out.println(
          "Invoked static method: " + args[1]
          + " of class: " + args[0]
          + " with no args\nResults: " + ret );
    } catch ( ClassNotFoundException e ) { 
      // Class.forName( ) can't find the class
    } catch ( NoSuchMethodException e2 ) { 
      // that method doesn't exist 
    } catch ( IllegalAccessException e3 ) { 
      // we don't have permission to invoke that method 
    } catch ( InvocationTargetException e4 ) { 
      // an exception occurred while invoking that method 
      System.out.println(
          "Method threw an: " + e4.getTargetException( ) );
    } 
  } 
}

We can run invoke to fetch the value of the system clock:

% java Invoke java.lang.System currentTimeMillis
Invoked static method: currentTimeMillis of class:
java.lang.System with no args  
Results: 861129235818

Our first task is to look up the specified Class by name. To do so, we call the forName( ) method with the name of the desired class (the first command-line argument). We then ask for the specified method by its name. getMethod( ) has two arguments: the first is the method name (the second command-line argument), and the second is an array of Class objects that specifies the method’s signature. (Remember that any method may be overloaded; you must specify the signature to make it clear which version you want.) Since our simple program calls only methods with no arguments, we create an anonymous empty array of Class objects. Had we wanted to invoke a method that takes arguments, we would have passed an array of the classes of their respective types, in the proper order. For primitive types we would have used the necessary wrappers. The classes of primitive types are represented by the static TYPE fields of their respective wrappers; for example, use Integer.TYPE for the class of an int.

Once we have the Method object, we call its invoke( ) method. This calls our target method and returns the result as an Object. To do anything nontrivial with this object, you have to cast it to something more specific. Presumably, since you’re calling the method, you know what kind of object to expect. If the returned value is a primitive type like int or boolean, it will be wrapped in the standard wrapper class for its type. (Wrappers for primitive types are discussed in Chapter 9.) If the method returns void, invoke( ) returns a Void object. This is the wrapper class that represents void return values.

The first argument to invoke( ) is the object on which we would like to invoke the method. If the method is static, there is no object, so we set the first argument to null. That’s the case in our example. The second argument is an array of objects to be passed as arguments to the method. The types of these should match the types specified in the call to getMethod( ). Because we’re calling a method with no arguments, we can pass null for the second argument to invoke( ). As with the return value, you must use wrapper classes for primitive argument types.

The exceptions shown in the previous code occur if we can’t find or don’t have permission to access the method. Additionally, an InvocationTargetException occurs if the method being invoked throws some kind of exception itself. You can find out what it threw by calling the getTargetException( ) method of InvocationTargetException.

Accessing Constructors

The java.lang.reflect.Constructor class represents an object constructor that accepts arguments. You can use it, subject to the security manager, to create a new instance of an object. (Recall that you can create instances of a class with Class.newInstance( ) , but you cannot specify arguments with that method.)

Here we’ll create an instance of java.util.Date, passing a string argument to the constructor:

try { 
    Constructor c =
        Date.class.getConstructor(new Class [] { String.class } );
    Object o = c.newInstance( new Object [] { "Jan 1, 2000" } ); 
    Date d = (Date)o; 
    System.out.println(d); 
} catch ( NoSuchMethodException e ) { 
    // getConstructor( ) couldn't find the constructor we described 
} catch ( InstantiationException e2 ) { 
    // the class is abstract  
} catch ( IllegalAccessException e3 ) { 
    // we don't have permission to create an instance 
} catch ( InvocationTargetException e4 ) { 
    // the construct threw an exception 
}

The story is much the same as with a method invocation; after all, a constructor is really no more than a method with some strange properties. We look up the appropriate constructor for our Date class—the one that takes a single String as its argument—by passing getConstructor( ) an array containing the String class as its only element. (If the constructor required more arguments, we would put additional objects in the array, representing the class of each argument.) We can then invoke newInstance( ), passing it a corresponding array of argument objects. Again, to pass primitive types, we would wrap them in their wrapper types first. Finally, we cast the resulting object to a Date and print it.

The exceptions from the previous example apply here, too, along with IllegalArgumentException and InstantiationException . The latter is thrown if the class is abstract, and so can’t be instantiated.

What About Arrays?

The reflection API allows you to create and inspect arrays of base types using the java.lang.reflect.Array class. The process is very much the same as with the other classes, so we won’t cover it here. For more information, look in your favorite Java language reference.

Dynamic Interface Adapters

Ideally, Java reflection would allow us to do everything at runtime that we can do at compile time (without forcing us to generate and compile source into byte-code). But prior to SDK 1.3, there was an important piece missing from the puzzle. Although we could dynamically load and create instances of objects at runtime using the Class.forName( ), there was no way to create new types or implementations of objects—for which no class files pre-exist—on the fly.

In SDK 1.3, the java.lang.reflect.Proxy class takes a step towards solving this problem, by allowing the creation of adapter objects that implement arbitrary interfaces. The Proxy class is a factory that can generate an adapter class implementing any interface you want. When methods are invoked on the adapter class, they are delegated to a designated InvocationHandler object. You can use this to create implementations of any kind of interface at runtime and handle the method calls anywhere you want. This is particularly important for tools that work with JavaBeans, which must dynamically register event listeners. (We’ll mention this again in Chapter 19.)

In the following snippet, we take an interface name and construct a proxy implementing the interface. It will output a message whenever any of the interface’s methods is invoked.

import java.lang.reflect.*;

InvocationHandler handler =
  new InvocationHandler( ) {
    public Object
    invoke( Object proxy, Method method, Object[] args ) {
        System.out.println( "Method: "+ method.getName() +"( )"
                            +" of interface: "+ interfaceName
                            + " invoked on proxy." );
        return null;
    }
  };

Class clas = Class.forName( interfaceName );

Object interfaceProxy =
    Proxy.newProxyInstance( clas.getClassLoader( ), 
                            new Class[] { clas }, handler );

The resulting object, interfaceProxy, can be cast to the type of the interface we specified in interfaceName. It will call our handler whenever any of its methods is called.

First we make an implementation of InvocationHandler. This is an object with an invoke( ) method that takes as its argument the Method being called and an array of objects representing the arguments to the method call. Then we fetch the class of the interface that we’re going to implement using Class.forName( ) . Finally we ask the proxy to create an adapter for us, specifying the types of interfaces (you can specify more than one) that we want implemented and the handler to use. invoke( ) is expected to return an object of the correct type for the method call. If it returns the wrong type, a special runtime exception is thrown. Any primitive types in the arguments or in the return value should be wrapped in the appropriate wrapper class. (The runtime system unwraps the return value, if necessary.)

What Is Reflection Good for?

In Chapter 19, we’ll learn how reflection is used to dynamically discover capabilities and features of Java Bean objects. But these are somewhat behind-the-scenes applications. What can reflection do for us in everyday situations?

Well, we could use reflection to go about acting as if Java had dynamic method invocation and other useful capabilities; in Chapter 19, we’ll also develop a dynamic adapter class using reflection. But as a general coding practice, dynamic method invocation is a bad idea. One of the primary features of Java is its strong typing and safety. You abandon much of that when you take a dip in the reflecting pool.

More appropriately, you can use reflection in situations where you need to work with objects that you can’t know about in advance. Reflection puts Java on a higher plane of programming languages, opening up possibilities for new kinds of applications. As we hinted earlier, one of the most important uses for reflection will be in integrating Java with scripting languages. With reflection, one could write a source code interpreter in Java that could access the full Java APIs, create objects, invoke methods, modify variables and do all of the other things that a Java program can do at compile time, while it is running. In fact someone has done this—one of the authors of this book!

The BeanShell application

Pat here . . . I can’t resist inserting a plug here for BeanShell—my free, open source, light-weight Java scripting language. BeanShell is just what I alluded to in the previous section—a Java application that uses the reflection API to execute Java statements and expressions dynamically. You can use BeanShell interactively to quickly try out some of the examples in this book (although you can’t create classes per se). BeanShell exercises the Java reflection API to its fullest and serves as a demonstration of how dynamic the Java runtime environment really is.

You can find a copy of BeanShell on the CD-ROM that accompanies this book, on the book’s web page, http://www.oreilly.com/catalog/learnjava, or at http://www.beanshell.org. See Appendix B, for more information on getting started. I hope you find it both interesting and useful!

Get Learning Java now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.