Name: boT120536 Date: 11/07/2000 java version "1.3.0" Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.0) Classic VM (build 1.3.0, J2RE 1.3.0 IBM build cx130-20000623 (JIT enabled: jitc)) I have a general complaint about how reflection handles Throwables during execution. InvocationTargetException is a checked error, yet it traps all Throwables instead of only checked errors. Thus, a user is required to catch Errors and RuntimeExceptions. This is a safe approach to take, but adds needless clutter -- the reason Error exists is so that users do not have to catch something that can be difficult or impossible to recover from unless they really mean to. I would like the reflection methods, like Constructor.newInstance(), to be more in line with how Class.newInstance() handles Errors (it does not trap or wrap them): if a user wants to worry about OutOfMemoryError during reflection, then they can explicitly catch it; but if they don't want to worry, it is not caught for them inside a checked exception. It would be nicer if Reflection methods wrapped any caught Throwable in one of three Throwables: InvocationTargetException (hereafter ITException, for brevity) - wraps any checked exception, and is checked so that it must be dealt with InvocationTargetRuntimeException (ITRuntimeException) - wraps unchecked runtime exceptions InvocationTargetError (ITError) - wraps any Error. This would be more like UndeclaredThrowableException, used by Proxy, which wraps only checked exceptions that fall through the cracks of a method's declaration. I can think of several methods for improving this, but each has its weaknesses: Scenario 1: ITUncheckedException extends ITException ITRuntimeException extends ITUncheckedException ITError extends ITUncheckedException This is backward compatible with existing class files, makes it easier to rethrow errors without conditionals in new code, but does not solve the problem of being forced to trap errors: try { Method.invoke()... } catch (InvocationTargetError err) { throw err.getTargetError(); } catch (InvocationTargetException ex) { ...//handle checked exceptions } Scenario 2: ITError extends Error ITRuntimeException extends RuntimeException This is also backward compatible with class files, and allows unchecked exceptions to remain unchecked, but if someone had been checking if the ITException wrapped a RuntimeException or Error, that check now fails. Scenario 3: interface ITException extends ThrowableInterface class ITCheckedException extends Exception implements ITException class ITRuntimeException extends RuntimeException implements ITException class ITError extends Error implements ITException This would require an addition to the JLS creating ThrowableInterface, which in addition to the class Throwable can be thrown and caught. (This addition could be useful, however, in other places). And, since ITException can presently be instantiated and is not a final class, any compiled code that directly creates an ITException or extends it would break. Scenario 4: interface UncheckedException class Error extends Throwable implements UncheckedException class RuntimeException extends Exception implements UncheckedException class ITRuntimeException extends ITException implements UncheckedException class ITError extends ITException implements UncheckedException class ITCheckedException extends ITException This would require a modification in the JLS and to new compilers so that any Throwable class is checked unless it implements UncheckedException. Of course, you will have to be careful on how UncheckedException works, maybe making it only a package interface in java.lang so that it won't be abused. In this way, it is possible to add any number of unchecked Throwables in core Java that do not extend Error or RuntimeException, which leaves room in Java for any future unchecked exceptions that are needed. This change would be backward compatible with all existing code: ITException is still a class, and code that catches ITException instead of a more specific subclass can still deal with any wrapped Throwable as it sees fit. Scenario 5: Modify ITException to ignore all unchecked exceptions; and only trap checked ones. This is compatible with existing classfiles, but again, if a user had been expecting a RuntimeException to be wrapped in an ITException, that check fails. Plus, it is nice knowing that the reflection methods can throw only a limited set of Exceptions, rather than any exception possible during the reflection. Scenario 6: Modify ITException to ignore only asynchronous exceptions (JLS 11.3.2) - those thrown by stop() in Threads, and InternalError and its subclasses. While this has the same problems as scenario 5 for changing user's expectations, it affects a smaller subset of exceptions, and the ones it would ignore should generally be ignored. Thus, threaded programs and virtual machine problems would have better behavior. Of all these scenarios, I think version 4 is the best solution to the problem. ============ // Example program - OutOfMemoryError is usually fatal and unrecoverable, so I // would rather not be forced to catch it wrapped inside the checked // InvocationTargetException import java.lang.reflect.*; class Memory { public static void main(String[] args) { try { System.out.println("Using Class.newInstance()"); Memory.class.newInstance(); } catch (Exception e) { System.out.println("Caught " + e); } catch (Error err) { System.out.println("ignored " + err); } try { System.out.println("Using java.lang.reflect"); Memory.class.getConstructor(null).newInstance(null); } catch (Exception e) { // As OutOfMemoryError is not an Exception, but an Error, // I shouldn't get here; but InvocationTargetException traps it System.out.println("Caught " + e); } catch (Error err) { System.out.println("ignored " + err); } try { System.out.println("Using direct call"); new Memory(); } catch (Error err) { System.out.println("ignored " + err); } } public Memory() { int size = 1; int[] array; while (true) { array = new int[size]; size *= 2; } // should eventually throw an OutOfMemoryError } } /* Produces this output: Using Class.newInstance() ignored java.lang.OutOfMemoryError Using java.lang.reflect Caught java.lang.reflect.InvocationTargetException Using direct call ignored java.lang.OutOfMemoryError */ (Review ID: 107972) ======================================================================
|