11

I stumbled upon code looking something like this:

void run() {
    try {
        doSomething();
    } catch (Exception ex) {
        System.out.println("Error: " + ex);
        throw ex;
    }
}

void doSomething() {
    throw new RuntimeException();
}

This code surprises me because it looks like the run()-method is capable of throwing an Exception, since it catches Exception and then rethrows it, but the method is not declared to throw Exception and apparently doesn't need to be. This code compiles just fine (in Java 11 at least).

My expectation would be that I would have to declare throws Exception in the run()-method.

Extra information

In a similar way, if doSomething is declared to throw IOException then only IOException needs to be declared in the run()-method, even though Exception is caught and rethrown.

void run() throws IOException {
    try {
        doSomething();
    } catch (Exception ex) {
        System.out.println("Error: " + ex);
        throw ex;
    }
}

void doSomething() throws IOException {
    // ... whatever code you may want ...
}

Question

Java usually likes clarity, what is the reason behind this behavior? Has it always been like this? What in the Java Language Specification allows the run() method not need to declare throws Exception in the code snippets above? (If I would add it, IntelliJ warns me that Exception is never thrown).

Simon Forsberg
  • 13,086
  • 10
  • 64
  • 108
  • *Side note: There's a bunch of questions on Stack Overflow about Java and exceptions, but I was not able to find a similar question to this one.* – Simon Forsberg Oct 16 '19 at 15:05
  • 3
    Interesting. What compiler you're using? If it's IDE compiler, then check with `javac` - I've been running into cases where Eclipse compiler was more lenient. – M. Prokhorov Oct 16 '19 at 15:15
  • 2
    I can reproduce this behaviour on openjdk-8. Notably compiling with the `-source 1.6` flag raises a compilation error as expected. Compiling with source compatibility 7 does **not** raise the compilation error – Vogel612 Oct 16 '19 at 15:16
  • 1
    seems like compiler is [smarter](https://docs.oracle.com/javase/8/docs/technotes/guides/language/catch-multiple.html) since Java 7 and does more checks on actual exception that could be thrown. – Michał Krzywański Oct 16 '19 at 15:16
  • 2
    This question is not a duplicate and the answer can be found in the link i provided `In detail, in Java SE 7 and later, when you declare one or more exception types in a catch clause, and rethrow the exception handled by this catch block, the compiler verifies that the type of the rethrown exception meets the following conditions : 1. 1. The try block is able to throw it. 2. There are no other preceding catch blocks that can handle it. 3. It is a subtype or supertype of one of the catch clause's exception parameters.` – Michał Krzywański Oct 16 '19 at 15:19
  • 2
    The [currently marked duplicate](https://stackoverflow.com/questions/46990866/more-precise-re-throw-exception-with-java-se-6-and-prior-versions) is definitely relevant, but doesn't provide a detailed enough answer IMO. There's *one* link to the JLS *in the comments to the answer* there, besides that no information. – Simon Forsberg Oct 16 '19 at 15:22
  • @michalk Those links should really be in an answer and not just in a comment. If you provide a better answer to the other one (or this one if it's reopened), I will upvote it. – Simon Forsberg Oct 16 '19 at 17:03
  • I guess I found a [duplicate](https://stackoverflow.com/questions/37185772/jdk-1-7-onwards-throwing-an-exception-object-from-catch-block-does-not-require) question which also is a duplicate but will answer your question – Michał Krzywański Oct 17 '19 at 18:06
  • Possible duplicate of [Rethrowing an Exception: Why does the method compile without a throws clause?](https://stackoverflow.com/questions/19913834/rethrowing-an-exception-why-does-the-method-compile-without-a-throws-clause) – Kaan Oct 18 '19 at 03:25

2 Answers2

0

I have not scan through the JLS as you have asked in your question, so please take this answer with a grain of salt. I wanted to make it a comment, but it would have been too big.


I find funny at times, how javac is pretty "smart" in some cases (like in your case), but leaves a lot of other things to be handled later by JIT. In this case, it is just that the compiler "can tell" that only a RuntimeException would be caught. This is obvious, it's the only thing you throw in doSomething. If you slightly change your code to:

void run() {
    try {
        doSomething();
    } catch (Exception ex) {
        Exception ex2 = new Exception();
        System.out.println("Error: " + ex);
        throw ex2;
    }
}

you will see a different behavior, because now javac can tell that there is a new Exception that you are throwing, un-related to the one you caught.

But things are far from ideal, you can "trick" the compiler yet again via:

void run() {
    try {
        doSomething();
    } catch (Exception ex) {
        Exception ex2 = new Exception();
        ex2 = ex;
        System.out.println("Error: " + ex);
        throw ex2;
    }
}

IMO, because of ex2 = ex; it should not fail again, but it does.

Just in case this was compiled with javac 13+33

Eugene
  • 117,005
  • 15
  • 201
  • 306
  • I read in some link that someone provided that if you reassign the caught exception in the catch-block, then the compiler is not able to be smart. I assume something similar applies in this case. The compiler knows that the `ex2` exception will be thrown, it was originally created as an `Exception` but is then reassigned to `ex`, and therefore the compiler is not able to be smart. – Simon Forsberg Oct 17 '19 at 17:57
  • @SimonForsberg someone that has a passion for `JLS` might come and provided the needed quotes to prove this; unfortunately I don't have them. – Eugene Oct 17 '19 at 18:01
  • For the record, when I change the catch block to contain a reassignment of the caught exception to itself (`ex = ex;`), the heuristic is no longer applied. This behaviour seems to be applied for all source levels from 7 through 11 and probably 13 – Vogel612 Oct 17 '19 at 18:05
  • Have a look at [this](https://stackoverflow.com/questions/37185772/jdk-1-7-onwards-throwing-an-exception-object-from-catch-block-does-not-require) question which is also a duplicate. This one and the dup of the possible dup explains it and also links to JLS. – Michał Krzywański Oct 17 '19 at 18:12
-1

Unchecked exceptions do not need to be declared in a method or constructor's throws clause if they can be thrown by the execution of the method or constructor and propagate outside the method or constructor boundary. RuntimeException is unchecked.

DRodriguez
  • 21
  • 2
  • This answer provides no additional information that hasn't already been shown in my question. I already know the difference between checked unchecked exception. – Simon Forsberg Oct 04 '22 at 12:20