Search code examples
javaexceptionlanguage-lawyerthrowchecked-exceptions

Why is throwing a checked exception type allowed in this case?


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. (?)


Solution

  • 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:

    A throw statement whose thrown expression is a final or effectively final exception parameter of a catch clause C can throw an exception class E iff:

    • E is an exception class that the try block of the try statement which declares C can throw; and
    • E is assignment compatible with any of C's catchable exception classes; and
    • E is not assignment compatible with any of the catchable exception classes of the catch clauses declared to the left of C in the same try statement.

    The first bullet point is the relevant one; because the catch-clause parameter t is effectively final (meaning that it's never assigned to or incremented or decremented; see §4.12.4 final Variables), throw t can only throw something that the try 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!