4

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.

Andy Turner
  • 137,514
  • 11
  • 162
  • 243
  • When I try to compile the `main` method with the empty `try` block, I get the error `incompatible types: Exception cannot be converted to Throwable`. What version of Java are you using? – Eli Sadoff Oct 21 '16 at 22:35
  • Related http://stackoverflow.com/questions/19913834/rethrowing-an-exception-why-does-the-method-compile-without-a-throws-clause – Tunaki Oct 21 '16 at 22:35
  • @EliSadoff the demo is on Ideone, so whatever they use. I think it's [sun-jdk-8u51](https://ideone.com/credits). – Andy Turner Oct 21 '16 at 22:38
  • @Tunaki related, but that only describes the phenomenon, without actually giving concrete pointers to JLS. – Andy Turner Oct 21 '16 at 22:38
  • @AndyTurner I'm running Java 9-ea and it doesn't compile there. – Eli Sadoff Oct 21 '16 at 22:39
  • @Andy Yep, hence only related :). – Tunaki Oct 21 '16 at 22:39
  • I guess the answer is going to be in [JLS Chapter 18](https://docs.oracle.com/javase/specs/jls/se8/html/jls-18.html). Gulp. – Andy Turner Oct 21 '16 at 22:46
  • 1
    This was covered at ["Why is throwing a checked exception type allowed in this case?"](http://stackoverflow.com/q/24981736/978917), but I won't close this as a dupe, because that one is for Java 7 and the details may have changed in Java 8. – ruakh Oct 21 '16 at 23:12
  • @ruakh yes, that describes it. Thanks. – Andy Turner Oct 21 '16 at 23:20

1 Answers1

4

I believe the very next paragraph of section 11.2.2 answers the question:

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

So, throw e; “can throw” only exceptions which the corresponding try-block “can throw,” where the latter is defined by the actual statements in the try-block.

Obviously an empty try-block does not qualify as a “can throw” section for any exception class. Your second example “can throw” NullPointerException, and since the catch-block “can throw” only the exception that the try-block “can throw,” the catch-block too can throw only the unchecked NullPointerException.

Your third example’s try-block “can throw” java.lang.Exception itself, therefore the catch-block “can throw” java.lang.Exception, so java.lang.Exception must be caught or declared to be thrown.

VGR
  • 40,506
  • 4
  • 48
  • 63