简体   繁体   中英

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

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:

public static demoRethrow() throws IOException {
    try {
        throw new IOException("Error");
    }
    catch(Exception exception) {
        /*
         * Do some handling and then rethrow.
         */
        throw exception;
    }
}

You can read the entire article explaining the changes here .

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!

This behavior is described in detail in the JLS in 11.2. Compile-Time Checking of Exceptions :

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.

(Emphasis mine.)

Your second example fails because t1 is not an "exception parameter of a catch clause".

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM