Consider the following code:
static void main(String[] args) {
try {
} catch (Exception e) {
throw e;
}
}
This code compiles without having to add throws Exception
to the method signature. (It behaves similarly with Throwable
in place of Exception
, too).
I understand why it can be run safely, in that Exception
can't actually be thrown in the try
block, so a checked exception cannot be thrown; I'm interested to know where this behaviour is specified.
It's not simply that the throw e
is never reached: the following code also compiles:
static void stillCompilesWithThrownUncheckedException() {
try {
throw new NullPointerException();
} catch (Exception e) {
throw e;
}
}
But if you throw a checked exception, it doesn't compile, as I expect:
static void doesNotCompileWithThrownCheckedException() {
try {
throw new Exception();
} catch (Exception e) {
throw e; // error: unreported exception Exception; must be caught or declared to be thrown
}
}
In JLS Sec 11.2.2, it says:
A
throw
statement (§14.18) whose thrown expression has static type E and is not a final or effectively final exception parameter can throw E or any exception class that the thrown expression can throw.
My interpretation of this statement is that throw e
can throw Exception
, because the static type of e
is Exception
. And then, in JLS Sec 11.2.3
:
It is a compile-time error if a method or constructor body can throw some exception class E when E is a checked exception class and E is not a subclass of some class declared in the throws clause of the method or constructor.
But it's not a compile-time error in the first two cases. Where is this behavior described in the language spec?
Edit: having marked it a dupe, I was going to ask the follow-up question: why isn't throw e;
considered unreachable in the first example.
The answer was much easier to find in JLS Sec 14.21:
A catch block C is reachable iff both of the following are true:
Either the type of C's parameter is an unchecked exception type or Exception or a superclass of Exception, or some expression or throw statement in the try block is reachable and can throw a checked exception whose type is assignable to the type of C's parameter. (An expression is reachable iff the innermost statement containing it is reachable.)
See §15.6 for normal and abrupt completion of expressions.
There is no earlier catch block A in the try statement such that the type of C's parameter is the same as or a subclass of the type of A's parameter.
Both of these are true (it's of type Exception
, and there's no earlier catch block), so it's "reachable". I guess the effort of calling out an empty try
block as a special case was too great for such a marginally-useful construct.