Home > Articles > Programming > Java

  • Print
  • + Share This
This chapter is from the book

3.3 Reflective Invocation

The Reflection API's ability to report the fields, methods, and constructors of a class is a very useful feature for the authors of Java development environments. For example, Java IDE wizards can automatically generate empty implementations of an interface for you to fill in. Java reflection makes this simple, whereas with many other languages, the development environment must build its own custom metadata representation to provide these services. While reflective reporting is a nifty parlor trick, the real power of reflection lies elsewhere, in the invocation APIs. With reflective invocation, you can access or change the value of a field and you can even invoke a method without any compile-time knowledge of the classes involved.

The invocation portion of the Reflection API uses the Field, Method, and Constructor classes to query and modify the state of Java classes and instances at runtime. This ability can be used to emulate function pointers, which do not exist in Java. Invocation can also be used as a substitute for inheritance. Instead of casting an object to a known interface type, you simply reflect against the class to see if it implements a particular method. Reflective invocation is also critical in crossing class loader boundaries. If you have a reference to a type that is not visible to your class loader delegation, you can use reflection to access that type's fields, constructors, and methods.

The reflective invocation APIs share several common elements. Because they are invoked on instances of Field, Method, or Constructor, they do not have an implicit this reference to the actual type being modified, and they must specify the this reference as an explicit parameter. Because reflective invocation APIs do not know the parameter types in advance, parameters are specified as Object or arrays of Object. Similarly, return types are always of type Object. Finally, reflective invocation cannot know in advance what checked exceptions might be thrown by a method, so all exceptions are caught by the APIs and rethrown as some wrapper type. The key reflective invocation APIs are summarized in Listing 3–13.

Listing 3–13 Key Reflective Invocation APIs

//all invocation APIs may throw
//IllegalArgumentException, IllegalAccessException

//from java.lang.reflect.Field
public Object get(Object this);
public void set(Object this, Object value);

//from java.lang.reflect.Method
public Object invoke(Object this, Object[] args)
    throws InvocationTargetException

//from java.lang.reflect.Constructor
public Object newInstance(Object[] args)
    throws InstantiationException, 
       InvocationTargetException

3.3.1 A Reflective Launcher

As an example of a situation in which the Reflection API is essential, consider the RunFromURL class shown in Listing 3–14. RunFromURL is very similar to the Java launcher except that it loads a class from a URL passed on the command line instead of from the classpath. The meat of the code is the run method, which uses getMethod to find a method with the signature main(String[] args) and then invokes that method with a null reference for this (since the method is assumed to be static)3 and an array of arguments from the command line. RunFromURL can execute the main method of any arbitrary class. Moreover, reflection is the only way this can be accomplished. RunFromURL cannot directly reference the class to be loaded. If RunFromURL referenced the class directly, implicit class loading would cause the class to be loaded from the classpath, which would defeat RunFromURL's ability to dynamically load classes from a location chosen at runtime.

Listing 3–14 RunFromURL

//imports, error checking removed for brevity
public class RunFromURL {
 public static void run(String url, String clsName,             
    String[] args) throws Exception 
 {
  URLClassLoader ucl = new URLClassLoader(new URL[]            
     {new URL(url)});
  Class cls = ucl.loadClass(clsName);
  Class argClass = String[].class;
  Method mainMeth = cls.getMethod("main", 
           new Class[]{argClass});
  mainMeth.invoke(null, new Object[]{args});
 }
 public static void main(String [] args) throws Exception
 {
  int argCount = args.length-2;
  String[] newArgs = new String[args.length-2];
  for (int n=0; n<argCount; n++) {
   newArgs[n] = args[n+2];
  }
  run(args[0], args[1], newArgs);
 } 
}

3.3.2 Wrapping Primitive Types

The syntax shown so far works fine for object types, but what about methods and fields that utilize primitive types? Java includes a set of primitive types (short, long, byte, boolean, double, float, int, and char) that can be represented directly in memory without the overhead of extending Object. These types pose a problem for reflection because the invocation APIs have generic signatures based on Object and Object[]. To enable the use of primitive types in reflective invocation, each primitive type in Java has a corresponding immutable class in the java.lang package. If you want to use reflection to call a method that takes a primitive type, you must use an instance of one of the wrapper classes instead. So for a hypothetical method

 int add(int n1, int n2);

you would use the following reflective syntax:

//to reflectively add ints n1 and n2
Object[] args = new Object[]{new Integer(n1),
               new Integer(n2)};
Integer temp = (Integer) addMethod.invoke(args);
int result = temp.intValue();

Before calling add, you must "box" the int parameters as Integers. Then, you must take the result of the call, cast it to Integer, and then "unbox" it to an int. This is inconvenient but necessary given the dual nature of numeric types in Java.

A related issue is the process of requesting a method with primitive types in its signature. Given that reflective invocation must use Integer wherever int was originally specified, how can getMethod distinguish between the two signatures shown in Listing 3–15? Even though both of these methods would be reflectively invoked with the same argument types, there is a way to distinguish between them to getMethod and related methods. Integer, like any other Java class, can be represented by its corresponding pseudo-literal, in this case Integer.class. For reflection purposes, the primitive types, such as int, have a special Class representation that is the static TYPE constant of the corresponding wrapper class. So, you should use Integer.class to request an Integer but Integer.TYPE to request an int, as Listing 3–15 demonstrates.

Listing 3–15 Finding Methods That Use Primitive Types

//two methods differing only in wrapper vs. primitive
public Integer add(Integer n1, Integer n2);
public int add(int n1, int n2);
 
//to access the wrapper version
Class[] wcls = new Class[]{Integer.class,Integer.class};
Method methWrapper = cls.getMethod("add", wcls);

//to access the primitive version
Class[] pcls = new Class[]{Integer.TYPE,Integer.TYPE};
Method primitiveWrapper = cls.getMethod("add", pcls);

3.3.3 Bypassing Language Access Rules

The rules for dealing with primitive types are arbitrary but raise no interesting design questions. A more challenging design issue is deciding how to enforce language access rules during reflective invocation. In normal Java programming, member access is controlled by the public, private, and protected keywords, plus the implied package-private setting if no keyword is specified. If you attempt to access a member whose access is restricted, compilation will fail. With reflective invocation, there is no way to know the protection modifier of a method at compile time. At runtime, the virtual machine can use class metadata to determine the protection modifier, and it can possibly prevent the operation being attempted.

If a piece of code attempts to use reflection to access a member that it would not have been allowed to access from compiled Java code, should the operation be allowed to proceed? The initial intuition is "no!" There are at least two good reasons to enforce the language protection rules during reflective invocation. First, reflection should not act as a back door to compromise encapsulation. It is both a design flaw and a security risk to open private members to code outside of a class. Second, reflection should obey the common-sense rule of least surprise. If the language works a certain way during normal method invocation, reflective invocation should mimic that behavior to the extent possible.

3.3.3.1 Using setAccessible

Despite these arguments in favor of limiting reflection, reflective code sometimes needs to bypass Java's protection modifiers in order to implement low-level subsystems that provide services for arbitrary Java classes. For example, one use of the Reflection API is for generic persistence services for instances. A generic persistence layer that did not persist the entire state of an object, including private members, would not be very generic. However, the design of the Reflection API also addresses the concerns raised previously about blithely accessing private data. The rules for language-level access checks provide something for everybody:

  1. By default, reflective invocation does not bypass language access checks. If you attempt to access a private, package-private, or protected member that you could not access normally, reflection will fail with an IllegalAc-cessException. This preserves the rule of least surprise.

  2. If you want to bypass language-level access checks, you can request this ability by calling setAccessible(true) on an AccessibleObject (see Listing 3–16). The reflection classes Field, Method, and Constructor all extend AccessibleObject. This allows a service like an XML serializer to gain access to all of an object's state.

Listing 3–16 AccessibleObject

// from java.lang.reflect.AccessibleObject
package java.lang.reflect;
public class AccessibleObject {
 //request permission to bypass access checks
 public void setAccessible(boolean flag) 
       throws SecurityException;
 //bypass access checks for several items in one shot
 public static void setAccessible(AccessibleObject[]
           arr, boolean flag);
 //remainder omitted for clarity
}

You can control which code is allowed to call setAccessible by installing a SecurityManager for a process.4 The setAccessible method provides a convenient chokepoint for a security check, and the default security implementation will prevent application code from calling setAccessible, while allowing system code to do so.

To see these rules in action, consider a Reporter's attempts to discover a Superhero's secret identity, shown in Listing 3–17. The Reporter wants to retrieve the Superhero's secretIdentity field via reflection, thus bypassing the fact that the field is private. If you execute this code as is, it will fail with an IllegalAccessException. In order to bypass the language check, add a call to secret.setAccessible(true) before the call to secret.get(s). With this call in place, reflection will bypass the language access check and return the private field. However, if you turn security on with the java.security.manager flag, the call to setAccessible will fail with an AccessControlException as it does here:

 >java –cp classes –Djava.security.manager Reporter
java.security.AccessControlException: access denied
(java.lang.reflect.ReflectPermission suppressAccessChecks)

Listing 3–17 Misusing Reflection to Access a Private Field

public class Superhero {
public class Superhero {
 public final String name;
 private final String secretIdentity;
 public Superhero(String name, String secretIdentity) {
  this.name = name;
  this.secretIdentity = secretIdentity;
 }
}
public class Reporter {
 public static void main(String [] args) 
     throws Exception {
  Superhero s = new Superhero("ReflectionMan", 
                "Brian Maso");
  hackIdentity(s);
 }
 public static void hackIdentity(Superhero s)
           throws Exception {
  Field secret = Superhero.class.
          getDeclaredField("secretIdentity");
  System.out.println("Identity is " + secret.get(s));
 }
}

To call setAccessible when security is enabled, you must have the suppressAccessChecks permission. By default, code that is in the core API or the extensions directory will have the suppressAccessChecks permission and be able to perform services such as serializing an object's private state. Application code loaded from the classpath or via a URLClassLoader will not have this permission, and therefore, it will be unable to inadvertently manipulate an object's implementation details.

3.3.3.2 Reflective Modification of Final Fields

Given that reflection sometimes has a legitimate reason to modify private fields, it is also logical to consider whether reflection might need to modify final fields. Before you look at how Java actually handles this today, consider the problem. Imagine a Person class that wanted to have some immutable fields:

public class Person {
 private final String name;
 private final String socialSecurityNo;
 //etc...
}

Now consider what would happen if you wanted to write a generic mechanism to instantiate Person from a row in a database. There are three approaches that might work:

  1. You could use reflection to instantiate a Person and then assign its fields. However, since some of the fields are final, this would require that the reflection APIs be designed with the ability to modify final fields. Many people argue that this ability is counter to the very definition of final.

  2. You could use a native method to instantiate a Person and then assign its fields. The Java Native Interface (JNI) includes APIs to do all sorts of things that would be illegal in Java, including instantiating objects without running their constructors and modifying final fields. The major downside to this approach is that you have to develop and deploy native code.

  3. You could require that the Person class provide an "all-fields constructor" that would simply pass through an initial value for every single field in the object. Generic services, such as the hypothetical database code, would use reflection to invoke this special constructor. The constructor probably would not be used during normal operation, and in fact, you could mark it private to guarantee this. This approach has neither of the disadvantages of the first two. Because constructors are allowed to set final fields, reflection does not need the ability to subvert final semantics. Also, no native code is required. The problem with this approach is that it requires the author of a class to explicitly write a special constructor in order to use serialization. The first two approaches simply leverage metadata and require no assistance from the classes that want generic services.

None of these solutions is ideal, and the Java language is still evolving in this area. Prior to SDK version 1.3, the first approach was used. The setAccessible method gave permission to modify final fields, and APIs such as serialization leveraged reflection. This led to some very confusing situations, as demonstrated in Listing 3–18.

Listing 3–18 Reflectively Modifying final Fields Is a Bad Idea.

import java.lang.reflect.*;
public class LimitedInt {
 public static void main(String[] args) throws Exception
 {
  System.out.println("Integer.MAX_VALUE=" + 
            Integer.MAX_VALUE);
  Field mv = Integer.class.getField("MAX_VALUE");
  mv.setAccessible(true);
  mv.set(null, new Integer(42));
  System.out.println("Retest: MAX_VALUE=" + 
            Integer.MAX_VALUE);
  System.out.println("Reflective: MAX_VALUE=" + 
            mv.get(null));
 }
}

Prior to SDK 1.3, setAccessible worked for final fields, and it was actually possible to change Integer.MAX_VALUE! This was made doubly confusing because Java compilers typically optimize static final primitive type declarations by inlining any access. So, even though you could change the value of MAX_VALUE seen by reflection, you could not change the value of MAX_VALUE where that constant was referenced in a class. On SDK 1.2, the code in Listing 3–18 prints the following:

 Integer.MAX_VALUE=2147483647
Retest: MAX_VALUE=2147483647
Reflective: MAX_VALUE=42

SDK 1.3 removed reflection's ability to change final fields. In the case of static final fields, this was definitely a step in the right direction. However, this also broke the approach to serialization that leveraged reflection to populate instance fields for classes like Person. In order to get around this new limitation, services like serialization switched to the second approach detailed earlier, using native methods to populate fields. This is not likely to be the final word on the subject. The proposed changes to Java's memory model would remove even the ability of native code to modify final fields (see [JMM] for details). If the new model is adopted, then the third approach might become necessary; this would require class authors to provide a special constructor for serializable classes that have final fields.

3.3.4 Exceptions Caused by Reflective Invocation

As mentioned above, attempts to bypass language restrictions without calling setAccessible(true) will generate an IllegalAccessException. IllegalAccessException is one of several exceptions that reflective invocation throws to indicate a problem that normal code would catch at compile time. Another example is IllegalArgumentException, used to indicate that a method argument is invalid. This will occur if one of the Objects in the array passed to invoke cannot be converted to the correct type for the underlying method. If you try to use reflection to instantiate an abstract class, Class will throw an InstantiationException.

Taken together, these three exception types are the reflection equivalent of the compiler's enforcement of language rules. While setAccessible can bypass the access rules, there is no way to get around the situations that cause an IllegalArgumentException or an InstantiationException. You must always pass arguments of the correct type, and you can never instantiate abstract classes.

Another situation where reflection may throw an exception is if the underlying method or constructor throws an exception. If this occurs, one of three things will happen:

  1. If a method throws an unchecked exception, such as an Error or Run-timeException, then this exception will be passed unmodified through the reflection layer to the caller. This behavior is no different from ordinary method invocation, where unchecked exceptions do not require an explicit catch block.

  2. A method may throw a declared checked exception. This is a problem for reflection because checked exceptions must be declared in a method signature, and reflection does not know a method's signature at compile time. The reflection APIs catch checked exceptions and convert them into a checked type InvocationTargetException that is declared in the signature of Method.invoke. This situation occurs frequently and leads to a special idiom on the part of the caller, as shown in Listing 3–19. InvocationTargetException is a reflection-provided wrapper class that can access the original exception via getTargetException. A client is typically more interested in the original exception, which indicates what went wrong, than it is in the InvocationTargetException, which only indicates that reflection was involved. As the sample shows, reflective clients will frequently catch InvocationTargetException, extract the original exception, cast it to some type expected by the caller, and rethrow.

  3. A method may throw a checked type that it did not declare. If this happens, invoke will convert the exception into the unchecked type UndeclaredThrowableException. This situation is extremely rare since the compiler will prevent a class from compiling if it tries to throw a checked exception that it did not declare. UndeclaredThrowableException indicates either a version mismatch caused by a partial recompile, or a corrupted binary class.

Listing 3–19 Extracting the "Real" Exception

//invokes some I/O method unknown at compile time
public void reflectiveIO(Method m, Object this, 
      String fileName) throws IOException
{
 try {
  m.invoke(this, new Object[]{filename});
  }
 catch (InvocationTargetException ite) {
  throw (IOException) ite.getTargetException(); 
 }
}
  • + Share This
  • 🔖 Save To Your Account