9

I know that generic types cannot extends Throwable and I understand that this doesn't make sense because type erasure (exceptions are thrown only at runtime) and other subtle things.

However Java allows a type parameter to be bounded by Throwable in fact the following code is legal

class MyClass<T extends Throwable> { /*body of the class */ }

What is illegal is the use of the T parameter in a catch clause

try { //do something that throws exception //  }
catch(T e) /* ILLEGAL!!! DOESN'T COMPILE */ {}

The only reason that I can understand about this constraint is that the type parameter, because erasure, is replaced by its bounding type, in this case Throwable. But type parameters could be bounded also by more than one bounding type so I could have this situation

class MyClass<T extends Object & Throwable> [*]

in such case T is replaced after erasure by Object and I know that an Object (that is not of type Throwable) variable cannot be inside a catch clause.

Let me know if this is the reason about this Java constraint. Thanks.

EDIT: [*] As davidxxx make me notice, this class declaration doesn't compile because Throwable is not an inteface.

Vin
  • 701
  • 1
  • 9
  • 30
  • 3
    `class MyClass` will not compile. You can only specify interfaces after the first type. So it should be rather `MyClass`. But even with it, you would have the same compiler restriction on the `catch` statement. – davidxxx Jul 24 '17 at 14:15
  • You are right. Throwable is not an interface. The "-able" suffix misled me. – Vin Jul 24 '17 at 14:21

1 Answers1

12

There is no way for the runtime to check if the type of a caught exception matches T because it does not know what T is.

See this question for some additional information - type parameters are kept on methods and classes, but not variables.

The point of a catch block is that you can handle exceptions by providing specific behavior to the exception type. This is also why you can supply multiple catch blocks for a single try. Without the specific exception type it would not be possible to execute the correct catch block.

Also, what do you expect the code to do if you run it with a raw type? The way you propose, T would be replaced with Throwable and the catch block would now catch all exceptions instead of only those of a certain type.

Additionally, the JLS specifies CatchType as:

CatchType:
    ClassType
    ClassType | CatchType

which does not include TypeArgument.

If you really want to do this, you can catch Throwable and check the type using instanceof or using its Class:

try {
    doSomethingDangerous();
} catch (Throwable t) {
    if (t instanceof IOException) {
        handleIoException(t);
    } else if (...) {
        ...
    } else {
        handleOther(t);
    }
}

Note: Catching Throwable or Exception is usually considered very bad practice.

Edit: here's what it looks like in C# for possible reference.

Salem
  • 13,516
  • 4
  • 51
  • 70
  • So, if I have `MyClass c=...` and if `T` was legal in `catch` clause, at runtime `catch {}` block would have handled also exceptions of different type such as `NullPointerException` or `Exception`, etc. Is it right? – Vin Jul 24 '17 at 14:39
  • @Vin Theoretically, a NPE _would_ be handled (but an `Exception` wouldn't because `RuntimeException` is actually a subclass of `Exception`.) For example, [it is possible to do this in C#](http://ideone.com/N9tXAs) if you want an example of what it would look like. – Salem Jul 24 '17 at 14:45
  • Ok. I don't understand about that `RuntimeException` is a subclass of `Exception`. Replacing `T` with `Throwable`, it doesn't matter what the actual type parameter is, catch block handle every type of `Throwable`. Theoretically speaking. – Vin Jul 24 '17 at 14:53
  • 1
    @Vin In your comment, the type parameter was `RuntimeException`, not `Throwable`. If you are referring to what would happen if the type parameter was _erased_, then yes it would likely be erased to `Throwable`. However, using type parameters in `catch` blocks is not (and probably will not be supported for a long time, if ever) supported, possibly _because_ of erasure, so this is just pure speculation. I provided a possible workaround if you need this functionality. If this answer helped, you can [accept an answer](https://stackoverflow.com/help/someone-answers). – Salem Jul 24 '17 at 14:57
  • I refer to the bounding type that is `Throwable`, and after erasure `T` is replaced by `Throwable`. Sorry if I explain it bad. – Vin Jul 24 '17 at 15:01
  • Answer accepted, but I would add another reason why Java made this illegal. Suppose `catch(T e)` is legal, in case of multi catch block this would have created problems. – Vin Jul 24 '17 at 15:13
  • Side note, you can't use type parameter in `if (e instanceof T)` so you should try a cast and catch the `ClassCastException`: `try { T t = (T)e; handleT(t); } catch (ClassCastException ex) { handleOther(e); /* e, not ex */ }`. If you *really* want to. – Matthieu Dec 11 '20 at 16:41