Search code examples
javaexceptiontry-catchchecked-exceptionsunchecked-exception

Why is catching checked exceptions allowed for code that does not throw exceptions?


In Java, methods that throw checked exceptions (Exception or its subtypes - IOException, InterruptedException, etc) must declare throws statement:

public abstract int read() throws IOException;

Methods that do not declare throws statement can't throw checked exceptions.

public int read() { // does not compile
    throw new IOException();
}
// Error: unreported exception java.io.IOException; must be caught or declared to be thrown

But catching checked exceptions in safe methods is still legal in java:

public void safeMethod() { System.out.println("I'm safe"); }

public void test() { // method guarantees not to throw checked exceptions
    try {
        safeMethod();
    } catch (Exception e) { // catching checked exception java.lang.Exception
        throw e; // so I can throw... a checked Exception?
    }
}

Actually, no. It's a bit funny: compiler knows that e is not a checked exception and allows to rethrow it. Things are even a bit ridiculous, this code does not compile:

public void test() { // guarantees not to throw checked exceptions
    try {
        safeMethod();
    } catch (Exception e) {        
        throw (Exception) e; // seriously?
    }
}
// Error: unreported exception java.lang.Exception; must be caught or declared to be thrown

The first snippet was a motivation for a question.

Compiler knows that checked exceptions can't be thrown inside a safe method - so maybe it should allow to catch only unchecked exceptions?


Returning to the main question - are there any reasons to implement catching checked exceptions in this way? Is it just a flaw in the design or am I missing some important factors - maybe backward incompatibilities? What could potentially go wrong if only RuntimeException were allowed to be catched in this scenario? Examples are greatly appreciated.


Solution

  • Quoting the Java Language Specification, §11.2.3:

    It is a compile-time error if a catch clause can catch checked exception class E1 and it is not the case that the try block corresponding to the catch clause can throw a checked exception class that is a subclass or superclass of E1, unless E1 is Exception or a superclass of Exception.

    I'm guessing that this rule originated long before Java 7, where multi-catches did not exist. Therefore, if you had a try block that could throw a multitude of exceptions, the easiest way to catch everything would be to catch a common superclass (in the worst case, Exception, or Throwable if you want to catch Errors as well).

    Note that you may not catch an exception type that is completely unrelated to what is actually thrown - in your example, catching any subclass of Throwable that is not a RuntimeException will be an error:

    try {
        System.out.println("hello");
    } catch (IOException e) {  // compilation error
        e.printStackTrace();
    }
    


    Edit by OP: The main part of the answer is the fact that question examples work only for Exception class. Generally catching checked exceptions is not allowed in random places of the code. Sorry if I confused somebody using these examples.