Why Doesn't Java Allow Generic Subclasses of Throwable

Why doesn't Java allow generic subclasses of Throwable?

As mark said, the types are not reifiable, which is a problem in the following case:

try {
doSomeStuff();
} catch (SomeException<Integer> e) {
// ignore that
} catch (SomeException<String> e) {
crashAndBurn()
}

Both SomeException<Integer> and SomeException<String> are erased to the same type, there is no way for the JVM to distinguish the exception instances, and therefore no way to tell which catch block should be executed.

Why doesn't Java support generic Throwables?

Java Language Specification
8.1.2 Generic Classes and Type Parameters:

This restriction is needed since the catch mechanism of the Java virtual machine works only with non-generic classes.

Personally, I think it's because we can't get any benefits of generics inside a catch clause. We can't write catch (Bouncy<String> ex) due to type erasure, but if we write catch (Bouncy ex), it would be useless to make it generic.

Making the inner class of a generic class extend Throwable

Java doesn't allow generic subclasses of Throwable. And, a nonstatic inner class is effectively parameterized by the type parameters of its outerclass (See Oracle JDK Bug 5086027). For instance, in your example, instances of your innerclass have types of form SomeGenericClass<T>.SomeInternalException. So, Java doesn't allow the static inner class of a generic class to extend Throwable.

A workaround would be to make SomeInternalException a static inner class. This is because if the innerclass is static its type won't be generic, i.e., SomeGenericClass.SomeInternalException.

public class SomeGenericClass<SomeType> {

public static class SomeInternalException extends RuntimeException {
[...]
}

[...]
}

How to restrict that subclass cannot be generic?

How it is restricted that subclass of java.lang.Throwable will not be
generic class?

Here's how OpenJDK compiler performs the check:

import com.sun.tools.javac.code.Symbol.*;   

private void attribClassBody(Env<AttrContext> env, ClassSymbol c) {
....

// Check that a generic class doesn't extend Throwable
if (!c.type.allparams().isEmpty() && types.isSubtype(c.type, syms.throwableType))
log.error(tree.extending.pos(), "generic.throwable");

As you can see forbidden type is kind of harcoded, so you can't use the same technique for your custom class without compiler code customization.

Full source code

Why can't I Throw or Catch Instances of a Generic Class in java?

You can't catch a generic exception, because the Java Language Specification explicitly forbids it. In 14.20 The try statement it says:

It is a compile-time error if a type variable is used in the denotation of the type of an exception parameter.

The section does not explain why, but the most like reason is that <T extends Throwable> is erased to Throwable, so your code would actually be compiled as if it was:

public static void test(Class<? extends Throwable> t){
try{
//do work
}
catch (Throwable e){
Logger.global.info(...)
}
}

This is not the intent expressed by your code (and very likely not what you want), therefor the language specification explicitly forbids it.

Note that throwing generic exceptions is allowed, but this is usually only relevant in the context of having some wrapper around another method that throws the exception.

How to add Generic class in Exception class handler?

Your example should work, you just need to do something like this

public class ResourceGeneralErrorTest extends RuntimeException {

private Object data;

public ResourceGeneralErrorTest(String message , Object error){
super(message);
this.data = error;
}

public Object getData() {
return this.data;
}

}

Edit: I ignored that you were extending RuntimeException, you cannot do that, generic classes not allowed to extend child of Throwable classes, but you can use Object there instead of T.

Custom Exception with generic type

You should probably use an interface to group the behaviour of your failed records. If there is a specific reason you can't do it, it is likely you have a problem with your design. In this case, you should make several exception classes.

Here is a good post to understand why Java doesn't allow subclasses of throwable : https://stackoverflow.com/a/501309/3425744

Why can't I have a catch for a checked exception for a call that throws a generic?

I think there is - based on the JSL - no good reason why you should not be able to catch your custom checked exception in your example.

If you read the cite from the JLS

It is a compile-time error if a catch clause can catch checked exception class E1 and it is not the case that the try block corresponding to the catch clause can throw a checked exception class that is a subclass or superclass of E1, unless E1 is Exception or a superclass of Exception.

A catch-clause should be allowed to catch any checked Exception, if a method in the corresponding try-block declares Throwable. In your example the try-block can throw a checked exception class that is a subclass or superclass of MyCheckedException namely Throwable and MyCheckedException is obviously not Exception or a superclass of Exception

This can easily be verified by removing the generics from the above example and see it compile without issues:

@FunctionalInterface
interface Processable<T> {
public T apply() throws Throwable;
}

private <T> T process(Processable<T> aProcessable) {
try {
return aProcessable.apply();
} catch (MyCheckedException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} catch (Throwable e) {
e.printStackTrace();
}

return null;
}

That is this problem somehow has to be related to the use of generics in combination with exceptions. Maybe this is related to type erasure, but with erased types your example works fine as well:

@FunctionalInterface
interface Processable {
public Object apply() throws Throwable;
}

private Object process(Processable aProcessable) {
try {
return aProcessable.apply();
} catch (MyCheckedException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} catch (Throwable e) {
e.printStackTrace();
}

return null;
}

Java - Why cannot type parameters be in a catch clause?

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.



Related Topics



Leave a reply



Submit