skip to content

How do you write a generic method that calls code which may throw various exceptions, given you can't catch a type parameter — and how does multi-catch fit in?

level: middleimportance: nice to knowfreq 18%

answer

  1. catch the bound, not T
  2. multi-catch: A | B, unrelated types only
  3. multi-catch var is implicitly final, type = LUB
  4. or just declare throws T / throws Exception
  5. instanceof to branch after a broad catch

basics

~20 s

Catch a concrete supertype (like Exception) instead of a type parameter, or list the specific exception types with multi-catch (catch (IOException | SQLException e)). You can also declare throws T and let the caller handle it.

solid answer

~40 s

Since `catch (T e)` is illegal, you handle exceptions in generic code with ordinary, reifiable catch types. Common patterns: (1) catch a concrete supertype that the type bound guarantees — for `<T extends IOException>` you can `catch (IOException e)`; (2) use **multi-catch** to handle several concrete types in one clause, `catch (IOException | SQLException e)`, where the variable `e` has the least-upper-bound type and is implicitly final; (3) don't catch at all — declare `throws T` (or `throws Exception`) and push handling to the caller, which is often the cleanest for generic utilities. The key constraint stays: every type that appears in a `catch` must be a concrete class known at runtime, never a type variable. Multi-catch types must also be unrelated (you can't list a type and its subtype together).

code

java · 9 lines
java
// Catch the erased bound; multi-catch unrelated checked types.
<T extends IOException> void process() {
    try {
        risky();           // may throw IOException (the bound) or SQLException
    } catch (IOException | SQLException e) {  // unrelated types, one clause
        // e is implicitly final; static type is their least upper bound
        throw new RuntimeException(e);
    }
}

go deeper

for a junior

Knows to catch a real exception class (like IOException or Exception), not T, and recognizes multi-catch syntax.

for a middle

Can choose between catching the bound, multi-catch, or declaring throws, and knows multi-catch types must be unrelated.

for a senior

Explains LUB typing and implicit-final of the multi-catch variable, and picks the cleanest pattern (often: declare and propagate) for a generic API.

for a principal

Designs generic exception-handling conventions for a library: when to expose checked vs unchecked, how bounds shape the API contract, and avoiding over-broad catches.

## Setup You've learned two restrictions: a generic class can't extend `Throwable`, and you can't write `catch (T e)`. So how do you actually write robust generic code that deals with exceptions? You use **concrete, reifiable** exception types in your catches and lean on declarations. ## Terms - **Bound**: in `<T extends Exception>`, `Exception` is the *upper bound*. At runtime `T` erases to this bound. - **Multi-catch**: a single `catch` clause handling several exception types separated by `|`, added in Java 7: `catch (A | B e)`. - **Least upper bound (LUB)**: the most specific common supertype; in multi-catch the caught variable's static type is the LUB of the listed types, and the variable is *effectively final* (you may not reassign it). ## Pattern 1 — catch the bound / a concrete supertype You cannot catch `T`, but you *can* catch a real class. If `T extends IOException`, then any thrown `T` is an `IOException`, so: ```java <T extends IOException> void read(Source<T> s) { try { s.open(); } catch (IOException e) { /* handles every T, since T <: IOException */ } } ``` If you have no useful bound, catch `Exception` or `RuntimeException` and branch with `instanceof` when you must distinguish. ## Pattern 2 — multi-catch for several concrete types When several *unrelated* checked types can fly out and you want the same handling: ```java try { doIo(); // throws IOException doSql(); // throws SQLException } catch (IOException | SQLException e) { // one clause, both types log.warn("failed", e); // e's static type is their LUB (Exception) // e = somethingElse; // illegal: multi-catch var is implicitly final } ``` Rules: the listed types must **not** be subtypes of one another (e.g. you can't write `IOException | FileNotFoundException`, because `FileNotFoundException` is already an `IOException` — redundant and rejected). The caught variable is implicitly final. ## Pattern 3 — don't catch; declare For a generic utility, often the right move is to propagate: ```java <T extends Throwable> R call(Callable<R> c) throws T { ... } ``` or simply `throws Exception`, letting the caller — who knows the concrete type — handle it. This sidesteps the catch restriction entirely. ## Putting it together The mental model: **catches deal in concrete runtime classes; generics deal in compile-time variables that get erased.** Bridge the two by catching the *erased bound* or specific classes (optionally several via multi-catch), or by declaring the exception away. Never try to make the `catch` itself generic.

  • Can you write `catch (IOException | FileNotFoundException e)`?
    No. FileNotFoundException is a subtype of IOException, so listing both is redundant and a compile error. Multi-catch requires the alternatives to be mutually unrelated (no subtype relationship).
  • Is the variable in a multi-catch mutable?
    No. The multi-catch parameter is implicitly final, so you cannot reassign it inside the block; its static type is the least upper bound of the listed exception types.

saying these in an interview costs you the question

  • Trying to 'fix' catch (T e) by adding a bound — still illegal.
  • Listing a type and its subtype in multi-catch (compile error).
  • Reassigning the multi-catch variable (it's implicitly final).
  • Catching Throwable/Exception broadly when a concrete type or declaration would be clearer and safer.

context