I have recently discovered and blogged about the fact that it is possible to sneak a checked exception through the javac compiler and throw it where it mustn't be thrown. This compiles and runs in Java 6 and 7, throwing a SQLException
without throws
or catch
clause:
public class Test { // No throws clause here public static void main(String[] args) { doThrow(new SQLException()); } static void doThrow(Exception e) { Test.<RuntimeException> doThrow0(e); } static <E extends Exception> void doThrow0(Exception e) throws E { throw (E) e; } }
The generated bytecode indicates that the JVM doesn't really care about checked / unchecked exceptions:
// Method descriptor #22 (Ljava/lang/Exception;)V // Stack: 1, Locals: 1 static void doThrow(java.lang.Exception e); 0 aload_0 [e] 1 invokestatic Test.doThrow0(java.lang.Exception) : void [25] 4 return Line numbers: [pc: 0, line: 11] [pc: 4, line: 12] Local variable table: [pc: 0, pc: 5] local: e index: 0 type: java.lang.Exception // Method descriptor #22 (Ljava/lang/Exception;)V // Signature: <E:Ljava/lang/Exception;>(Ljava/lang/Exception;)V^TE; // Stack: 1, Locals: 1 static void doThrow0(java.lang.Exception e) throws java.lang.Exception; 0 aload_0 [e] 1 athrow Line numbers: [pc: 0, line: 16] Local variable table: [pc: 0, pc: 2] local: e index: 0 type: java.lang.Exception
The JVM accepting this is one thing. But I have some doubts whether Java-the-language should. Which parts of the JLS justify this behaviour? Is it a bug? Or a well-hidden "feature" of the Java language?
My feelings are:
doThrow0()
's <E>
is bound to RuntimeException
in doThrow()
. Hence, no throws
clause along the lines of JLS §11.2 is needed in doThrow()
.RuntimeException
is assignment-compatible with Exception
, hence no cast (which would result in a ClassCastException
) is generated by the compiler.Difference Between Checked and Unchecked Exceptions in JavaA checked exception is caught at compile time whereas a runtime or unchecked exception is, as it states, at runtime. A checked exception must be handled either by re-throwing or with a try catch block, whereas an unchecked isn't required to be handled.
An unchecked exception is an exception that occurs at the time of execution. These are also called as Runtime Exceptions. These include programming bugs, such as logic errors or improper use of an API. Runtime exceptions are ignored at the time of compilation.
Here are the few unchecked exception classes: NullPointerException. ArrayIndexOutOfBoundsException. ArithmeticException.
All this amounts to exploiting the loophole that an unchecked cast to a generic type is not a compiler error. Your code is explicitly made type-unsafe if it contains such an expression. And since the checking of checked exceptions is strictly a compile-time procedure, nothing will break in the runtime.
The answer from the authors of Generics would most probably be along the lines of "If you are using unchecked casts, it's your problem".
I see something quite positive in your discovery—a break out of the stronghold of checked exceptions. Unfortunately, this can't turn existing checked-exception-poisoned APIs into something more pleasant to use.
At a typical layered application project of mine there will be a lot of boilerplate like this:
try { ... business logic stuff ... } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); }
Why do I do it? Simple: there are no business-value exceptions to catch; any exception is a symptom of a runtime error. The exception must be propagated up the call stack towards the global exception barrier. With Lukas' fine contribution I can now write
try { ... business logic stuff ... } catch (Exception e) { throwUnchecked(e); }
This may not seem like much, but the benefits accumulate after repeating it 100 times throughout the project.
In my projects there is high discipline regarding exceptions so this is a nice fit for them. This kind of trickery is not something to adopt as a general coding principle. Wrapping the exception is still the only safe option in many cases.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With