Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 307
AdamSkywalker Avatar asked Feb 03 '16 17:02

AdamSkywalker


People also ask

Do checked exceptions need to be thrown?

A checked exception must be handled either by re-throwing or with a try catch block, whereas an unchecked isn't required to be handled.

Why should we catch exceptions?

You should catch the exception when you are in the method that knows what to do. For example, forget about how it actually works for the moment, let's say you are writing a library for opening and reading files. Here, the programmer knows what to do, so they catch the exception and handle it.

Can exceptions be caught and thrown?

A thrown object may match several catch block but only the first catch block that matches the object will be executed. A catch-block will catch a thrown exception if and only if: the thrown exception object is the same as the exception object specified by the catch-block.

What happens if a thrown exception is not caught?

What happens if an exception is not caught? If an exception is not caught (with a catch block), the runtime system will abort the program (i.e. crash) and an exception message will print to the console. The message typically includes: name of exception type.


2 Answers

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.
like image 136
Aasmund Eldhuset Avatar answered Sep 19 '22 12:09

Aasmund Eldhuset


Java 7 introduced more inclusive exception type checking.

However, in Java SE 7, you can specify the exception types FirstException and SecondException in the throws clause in the rethrowException method declaration. The Java SE 7 compiler can determine that the exception thrown by the statement throw e must have come from the try block, and the only exceptions thrown by the try block can be FirstException and SecondException.

This passage is talking about a try block that specifically throws FirstException and SecondException; even though the catch block throws Exception, the method only needs to declare that it throws FirstException and SecondException, not Exception:

public void rethrowException(String exceptionName)  throws FirstException, SecondException {    try {      // ...    }    catch (Exception e) {      throw e;    }  } 

This means that the compiler can detect that the only possible exception types thrown in test are Errors or RuntimeExceptions, neither of which need to be caught. When you throw e;, it can tell, even when the static type is Exception, that it doesn't need to be declared or re-caught.

But when you cast it to Exception, this bypasses that logic. Now the compiler treats it as an ordinary Exception which needs to be caught or declared.

The main reason for adding this logic to the compiler was to allow the programmer to specify only specific subtypes in the throws clause when rethrowing a general Exception catching those specific subtypes. However, in this case, it allows you to catch a general Exception and not have to declare any exception in a throws clause, because no specific types that can be thrown are checked exceptions.

like image 40
rgettman Avatar answered Sep 20 '22 12:09

rgettman