Why can't the exception types listed in a multi-catch clause be in a subtype/supertype relationship with each other?
answer
- subtype is already covered by supertype
- redundant = dead code = compile error
- mirrors 'unreachable catch' rule
- unrelated branches are fine
- keep only the broader type to fix
basics
~10 sBecause one type would already cover the other. Listing both is redundant — the subtype is always also the supertype — so the compiler rejects it as a compile error to prevent meaningless code.
solid answer
~50 sIn a multi-catch clause the listed types must be disjoint: none may be a subclass of another. If you wrote catch (IOException | FileNotFoundException e), FileNotFoundException is already a subclass of IOException, so the broader IOException alone would catch it — the second type is completely redundant. The Java compiler treats this redundancy as a compile-time error rather than silently ignoring it, because it almost always signals a mistake. The same rule echoes the older 'unreachable catch' error you get when a narrower catch block follows a broader one. So the constraint isn't about safety, it's about catching dead, meaningless code early. The fix is to list only the broader type, or only the genuinely independent types. Types from completely separate branches of the hierarchy (like IOException and SQLException, which only share Exception) are fine.
go deeper
Recognizes that one type 'covers' another and that listing both is wrong, even if shaky on the exact rationale.
States the rule precisely (no listed type may subclass another) and that it's a compile-time redundancy error, with a correct fix.
Connects it to the broader 'unreachable/dead catch' family of compiler diagnostics and explains it's about provable redundancy, not safety.
Discusses how such static guards keep handling intent explicit and reviewable at scale, and contrasts with languages that silently allow redundant handlers.
## Background: the exception class hierarchy Java exceptions form an inheritance tree rooted at `Throwable`. For example `FileNotFoundException` **extends** `IOException`, which extends `Exception`, which extends `Throwable`. A `catch (IOException e)` block catches an `IOException` **and any of its subclasses**, because of the *is-a* relationship — a `FileNotFoundException` *is an* `IOException`. ## The rule In a multi-catch clause `catch (A | B | ... e)`, **no listed type may be a subtype (subclass) of another listed type**. Violating it is a **compile-time error**, e.g.: ```java try { read(); } catch (IOException | FileNotFoundException e) { // COMPILE ERROR ... } // error: Alternatives in a multi-catch statement cannot be related by subclassing // (FileNotFoundException is a subclass of IOException) ``` ## Why the rule exists — redundancy Because `IOException` already catches every `FileNotFoundException`, listing `FileNotFoundException` as a *second* alternative adds **nothing**: every object the second type would catch is already caught by the first. The type is **dead** — unreachable, meaningless. Java has a long-standing principle of rejecting provably useless code in `catch` constructs. The classic instance is the **unreachable catch** error with separate blocks: ```java try { read(); } catch (IOException e) { ... } catch (FileNotFoundException e) { ... } // COMPILE ERROR: already caught above ``` Multi-catch carries the same spirit: rather than silently accept a redundant alternative, the compiler flags it so you fix the obvious mistake (you probably meant a different, independent type, or just the broad one). ## What IS allowed Types on **separate branches** of the hierarchy — whose only common ancestor is something general like `Exception` — are perfectly legal, because neither catches the other: ```java catch (IOException | SQLException e) { ... } // OK: unrelated branches ``` The shared *supertype* (here `Exception`) is fine and expected — that's what the variable's type becomes. The forbidden thing is a **direct ancestor/descendant relationship between the listed alternatives themselves**. ## How to fix a violation - If you truly want everything, keep **only the broader** type: `catch (IOException e)`. - If you meant two distinct error families, replace the redundant one with the type you actually intended. ## Key takeaway The restriction is not a safety mechanism — it's a **redundancy/dead-code guard**. Listed multi-catch alternatives must be mutually exclusive in coverage; a subtype/supertype pair is not, so it's rejected.
- Is catch (Exception | IOException e) legal?No — IOException is a subclass of Exception, so it's redundant and a compile error. Use catch (Exception e) alone.
- How do you fix catch (IOException | FileNotFoundException e)?Drop the redundant FileNotFoundException and write catch (IOException e), since IOException already catches it.
saying these in an interview costs you the question
- Claiming it's a runtime restriction rather than a compile-time error
- Saying any two related-by-Exception types are forbidden (only direct sub/supertype pairs are)
- Thinking the rule exists for type-safety rather than redundancy
- Believing you can list both and the JVM just picks one