What are the special unreachability rules for catch blocks and checked exceptions?
answer
- catch a checked type the try can't throw -> error
- RuntimeException/Error/Exception/Throwable always catchable
- Unchecked can be thrown anywhere -> never provably absent
- Specific catch after broader one -> unreachable
- Multi-catch alternatives must be disjoint (no subtype of another)
basics
~20 sA catch block is a compile error if its exception type can never be thrown by the try body. For checked exceptions, you can't catch a type the try can't throw; but you can always catch RuntimeException, Error, or Exception, because those can occur anywhere.
solid answer
~50 sJava treats a `catch` clause as unreachable — a compile error — if the try block cannot throw a *checked* exception assignable to that catch's type. So `catch (IOException e)` is illegal if nothing in the try declares or throws an IOException. This is a deliberate reachability rule that stops you catching impossible checked exceptions. Crucially, the rule applies only to **checked** exceptions: you may always catch `RuntimeException`, `Error`, `Throwable`, or `Exception` (the latter because unchecked exceptions are subtypes of it), since unchecked exceptions and errors can be thrown implicitly anywhere. Two related rules: a more specific catch placed *after* a broader one that already covers it is unreachable (e.g. `catch (Exception)` then `catch (IOException)`); and in a multi-catch `catch (A | B e)`, neither type may be a subtype of the other. These rules keep exception handling honest about what can actually occur.
code
java · 5 linestry {
System.out.println("no IO");
} catch (IOException e) { // ERROR: IOException is never thrown in the try
e.printStackTrace();
}go deeper
Knows catch must order specific exceptions before general ones and that you can catch Exception/RuntimeException.
Knows catching a checked exception the try can't throw is an error, and the supertype-first ordering rule.
Distinguishes checked vs. unchecked behavior, explains why unchecked/Exception catches are always allowed, and states the multi-catch disjointness rule.
Relates these rules to the broader checked-exception design, reasons about reachability proofs the compiler performs, and weighs the design trade-offs (catch-honesty vs. flexibility) including controversies around checked exceptions.
## Background: checked vs. unchecked exceptions Java exceptions split into two families: - **Checked exceptions** (subclasses of `Exception` but not `RuntimeException`, e.g. `IOException`): the compiler *tracks* these. A method must declare them with `throws` or handle them. The compiler knows exactly which checked exceptions a piece of code can produce. - **Unchecked exceptions** (`RuntimeException` and its subclasses, like `NullPointerException`) and **errors** (`Error`, like `OutOfMemoryError`): these can be thrown *implicitly, anywhere* — a null dereference, a division by zero, the JVM running out of memory. The compiler does not track them. ## The catch reachability rule The JLS says a `catch` clause is **unreachable (a compile error)** if it is impossible for the try block to throw a *checked* exception that is assignable to the caught type. In plain terms: you cannot catch a checked exception that the try block provably cannot throw. ```java try { System.out.println("no IO here"); } catch (IOException e) { // ERROR: IOException can never be thrown by the try } ``` Nothing in the try declares or throws `IOException`, so catching it is unreachable. This rule forces your catch clauses to correspond to exceptions that can genuinely occur, preventing dead handlers and copy-paste mistakes. ## Why unchecked types are always allowed Because unchecked exceptions and errors can arise *anywhere*, the compiler can never prove they won't be thrown. So these catches are **always reachable**: ```java try { trivial(); } catch (RuntimeException e) {} // always OK catch (Error e) {} // always OK catch (Exception e) {} // OK (covers unchecked subtypes too) catch (Throwable t) {} // always OK ``` Note the subtlety: `catch (Exception e)` is allowed even on a try that throws no checked exception, because `RuntimeException extends Exception`, and runtime exceptions can occur anywhere. Only catching a *specific checked* type the try can't throw is the error. ## Related rule 1: ordering of catch clauses A catch for a subtype placed **after** a catch for its supertype is unreachable, because the earlier, broader catch already handles it: ```java try { readFile(); } catch (Exception e) {} // catches everything first catch (IOException e) {} // ERROR: already caught by Exception above ``` Most specific exceptions must come first. ## Related rule 2: multi-catch Java 7+ allows `catch (A | B e)` to handle several types in one block. The rule: the alternatives must be **disjoint** — none may be a subtype of another: ```java catch (IOException | Exception e) {} // ERROR: IOException is a subtype of Exception ``` If one already covered the other, listing both is redundant and rejected. ## Why this matters These rules make the type system enforce that every handler can actually fire. Combined with checked-exception tracking, they catch a whole class of bugs at compile time: handlers for impossible exceptions, shadowed handlers, and redundant multi-catch alternatives — all surfaced as unreachable/illegal-catch errors rather than dead code that silently never runs.
- Why is 'catch (Exception e)' allowed even when the try throws no checked exception?Because RuntimeException (and Error via Throwable) are subtypes that can be thrown anywhere implicitly, the compiler can never prove an Exception won't occur. Only a specific *checked* type the try can't throw is an error.
- What's wrong with 'catch (IOException | FileNotFoundException e)'?FileNotFoundException is a subtype of IOException, so the alternatives are not disjoint. Multi-catch requires that no alternative be a subtype of another; this is a compile error.
saying these in an interview costs you the question
- Claiming you can never catch Exception on a try with no checked throws (you can — unchecked subtypes)
- Thinking the rule applies to unchecked exceptions too
- Putting a subtype catch before/after a supertype without knowing the ordering rule
- Listing 'IOException | Exception' in a multi-catch