Monday, March 28, 2011

JDK 7: Reflection Exception Handling with ReflectiveOperationException and Multi-Catch

Java 7 adds a new exception class called ReflectiveOperationException. The Javadoc documentation describes this class as a "Common superclass of exceptions thrown by reflective operations in core reflection." I look at this new exception class and discuss advantages of using it in this blog post and compare it to Java 7's more general approach for handling multiple exceptions in a single catch clause.

The ReflectiveOperationException is a checked exception class extended by older (available prior to 1.7) exception classes ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchFieldException, and NoSuchMethodException. There is an obvious advantage to having a checked exception that is a parent to all of these pre-existing checked exceptions that could be thrown by reflection operations: it is much easier to "catch" a single exception or declare a "throws" clause for a single exception rather than for all of the checked reflection exceptions.

The new JDK 7 feature supporting handling of multiple exceptions with a single catch block might also be used in this situation, but I like this general reflection operation exception approach. The following code examples demonstrate all three approaches (catching each individual checked exception, catching the new checked parent exception ReflectiveOperationException, and using the new JDK 7 multiple exception catching mechanism).

Traditional Individual Reflection Exception Handling
/**
    * Instantiate an instance of Person using the provided parameters and invoke
    * its toString() method with code using the traditional catching of
    * individual checked exceptions related to the various Java reflection calls.
    *
    * @param newLastName Last name of Person instance to be instantiated.
    * @param newFirstName First name of Person instance to be instantiated.
    * @param newAge Age of Person instance to be instantiated.
    */
   public void instantiatePersonReflectivelyTraditionally(
      final String newLastName, final String newFirstName, final int newAge)
   {
      final String className = "dustin.examples.Person";
      final String methodName = "toString";
      try
      {
         final Class clazz = Class.forName(className);
         final Class[] ctorParams = {String.class, String.class, Integer.TYPE};
         final Constructor ctor = clazz.getConstructor(ctorParams);
         final Person person = (Person) ctor.newInstance(newLastName, newFirstName, newAge);
         final Method toStringMethod = clazz.getDeclaredMethod(methodName);
         out.println(toStringMethod.invoke(person));
      }
      catch (ClassNotFoundException cnfEx)
      {
         err.println("Unable to instantiate class " + className + ": " + cnfEx);
      }
      catch (NoSuchMethodException nsmEx)
      {
         err.println("No method " + className + "." + methodName + ": " + nsmEx);
      }
      catch (IllegalAccessException illAccEx)
      {
         err.println(
              "IllegalAccessException encountered invoking " + className + "."
            + methodName + ": " + illAccEx);
      }
      catch (InvocationTargetException invokeTargetEx)
      {
         err.println(
              "InvocationTargetException encountered invoking " + className + "."
            + methodName + ": " + invokeTargetEx);
      }
      catch (InstantiationException ctorEx)
      {
         err.println("Unable to instantiate " + className + ": " + ctorEx);
      }
   }

Java 7: Catching Single ReflectiveOperationException
/**
    * Instantiate an instance of Person using the provided parameters and invoke
    * its toString() method with code that only needs to capture the single
    * exception ReflectiveOperationException available with Java 7.
    *
    * @param newLastName Last name of Person instance to be instantiated.
    * @param newFirstName First name of Person instance to be instantiated.
    * @param newAge Age of Person instance to be instantiated.
    */
   public void instantiatePersonReflectivelyJava7ReflectiveOperationException(
      final String newLastName, final String newFirstName, final int newAge)
   {
      final String className = "dustin.examples.Person";
      final String methodName = "toString";
      try
      {
         final Class clazz = Class.forName(className);
         final Class[] ctorParams = {String.class, String.class, Integer.TYPE};
         final Constructor ctor = clazz.getConstructor(ctorParams);
         final Person person = (Person) ctor.newInstance(newLastName, newFirstName, newAge);
         final Method toStringMethod = clazz.getDeclaredMethod(methodName);
         out.println(toStringMethod.invoke(person));
      }
      catch (ReflectiveOperationException reflectOpEx)  // single exception!
      {
         err.println(
              "Reflection error trying to invoke " + className + "."
            + methodName + ": " + reflectOpEx);
      }
   }

Java 7: Catching Multiple Reflection Exceptions with Single Catch
/**
    * Instantiate an instance of Person using the provided parameters and invoke
    * its toString() method with code using the Java 7 language feature allowing
    * multiple exceptions to be explicitly specified for capture in a single
    * catch block.
    *
    * @param newLastName Last name of Person instance to be instantiated.
    * @param newFirstName First name of Person instance to be instantiated.
    * @param newAge Age of Person instance to be instantiated.
    */
   public void instantiatePersonReflectivelyJava7MultiCatch(
      final String newLastName, final String newFirstName, final int newAge)
   {
      final String className = "dustin.examples.Person";
      final String methodName = "toString";
      try
      {
         final Class clazz = Class.forName(className);
         final Class[] ctorParams = {String.class, String.class, Integer.TYPE};
         final Constructor ctor = clazz.getConstructor(ctorParams);
         final Person person = (Person) ctor.newInstance(newLastName, newFirstName, newAge);
         final Method toStringMethod = clazz.getDeclaredMethod(methodName);
         out.println(toStringMethod.invoke(person));
      }
      catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException reflectionEx)
      {
         err.println(
              "Reflection error trying to invoke " + className + "."
            + methodName + ": " + reflectionEx);
      }
   }

I didn't show it here because it's considered "bad form," but another option would have been to catch generic Exception as the singly caught exception (similar to example catching only ReflectiveOperationException but much broader). I generally prefer to catch an exception as specific to the situation as possible. Assuming the exception handling is the same for all of the relevant exceptions, I prefer not to list them each individually. This makes the approach of catching the new ReflectiveOperationException or the new Java 7 multiple exception catching mechanism most appealing. I slightly prefer catching ReflectiveOperationException when appropriate (reflection), but the multiple exception catching mechanism is a more general tactic for situations where no convenient parent exception exists.


Conclusion

JDK 7 provides two new and better mechanisms for handling exceptions commonly thrown when using Java reflection. The new ability to catch multiple exceptions with a single catch clause is generally useful for catching multiple exceptions that are to be handled the same way. The availability of ReflectiveOperationException in Java 7 is a specific exception parent class that can be caught to handle all its child reflection exception classes in a single catch.

4 comments:

Martijn Verburg said...

This is interesting given that MethodHandles are coming into Java 7. MethodHandles don't cover all cases that Reflection currently covers but is already being seen as a better way to do Reflection.

See this blog post for details.

The multi-catch is also an interesting project coin feature. Exercise for the reader: "What will an IDE present when you are looking for all of the methods on e in a multi-catch catch block?"

Thanks for another interesting post Dustin!

Kevin said...

Checked exceptions? Pshaw! Lets move Java out of this tar pit already. :-)

Dustin said...

Martijn,

Thanks for pointing out the post A glimpse at MethodHandle and its usage. I'll definitely check it out as my only understanding of this new feature has been a vague recollection of it described at a high level.

I look forward to trying out NetBeans 7.0 on your "exercise for the reader." My 6.9 installation compiles the multi-catch fine when I select "compile" or "build," but the automatic syntax checking does not like it and marks it with the normal syntax error highlighting.

Dustin

Dustin said...

Besides being useful for catching the reflection-related exceptions with a single exception, the ReflectiveOperationException also provides real-life insight into Java-related issues that API designers must consider when changing things that affect public contracts. Joseph D. Darcy's blog post Reflective Operation Exceptions talks about some of these issues related to ReflectiveOperationException.