I noticed by accident that this throw
statement (extracted from some more complex code) compiles:
void foo() {
try {
} catch (Throwable t) {
throw t;
}
}
For a brief but happy moment I thought that checked exceptions had finally decided to just die already, but it still gets uppity at this:
void foo() {
try {
} catch (Throwable t) {
Throwable t1 = t;
throw t1;
}
}
The try
block doesn't have to be empty; it seems it can have code so long as that code doesn't throw a checked exception. That seems reasonable, but my question is, what rule in the language specification describes this behavior? As far as I can see, §14.18 The throw Statement explicitly forbids it, because the type of the t
expression is a checked exception, and it's not caught or declared to be thrown. (?)
I think that the wording in §14.18 The
throw
Statement, that you refer to, is a mistake in the JLS — text that should have been updated with Java SE 7, and was not.The bit of JLS text that describes the intended behavior is in §11.2.2 Exception Analysis of Statements:
The first bullet point is the relevant one; because the
catch
-clause parametert
is effectively final (meaning that it's never assigned to or incremented or decremented; see §4.12.4final
Variables),throw t
can only throw something that thetry
block could throw.But as you say, the compile-time checking in §14.18 does not make any allowance for this. §11.2.2 does not decide what's allowed and what's not; rather, it's supposed to be an analysis of the consequences of the various restrictions on what can be thrown. (This analysis does feed back into more-normative parts of the spec — §14.18 itself uses it in its second bullet point — but §14.18 can't just say "it's a compile-time error if it throws an exception it can't throw per §11.2.2", because that would be circular.)
So I think §14.18 needs to be adjusted to accommodate the intent of §11.2.2.
Good find!
This behavior is described in detail in the JLS in 11.2. Compile-Time Checking of Exceptions:
(Emphasis mine.)
Your second example fails because
t1
is not an "exception parameter of acatch
clause".This is because of a change that was included in Project Coin, introduced in Java 7, to allow for general exception handling with rethrowing of the original exception. Here is an example that works in Java 7 but not Java 6:
You can read the entire article explaining the changes here.