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.
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 Error
s 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();
}