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?
answer
- catch the bound, not T
- multi-catch: A | B, unrelated types only
- multi-catch var is implicitly final, type = LUB
- or just declare throws T / throws Exception
- instanceof to branch after a broad catch
basics
~20 sCatch 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 sSince `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// 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
Knows to catch a real exception class (like IOException or Exception), not T, and recognizes multi-catch syntax.
Can choose between catching the bound, multi-catch, or declaring throws, and knows multi-catch types must be unrelated.
Explains LUB typing and implicit-final of the multi-catch variable, and picks the cleanest pattern (often: declare and propagate) for a generic API.
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.