为什么Java不允许Throwable的通用子类?

根据Java语言select第3版:

如果generics类是Throwable的直接或间接子类,那么这是一个编译时错误。

我想了解为什么做出这个决定。 genericsexception有什么问题?

(据我所知,generics只是编译时语法糖,在.class文件中它们会被翻译成Object ,所以有效地声明一个generics类就好像它的一切都是一个Object 。我错了。)

正如马克所说,这些types是不可确定的,这在以下情况下是一个问题:

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

SomeException<Integer>SomeException<String>都被擦除为相同types,JVM无法区分exception实例,因此无法分辨哪个catch块应该执行。

这里是一个简单的例子,说明如何使用exception:

 class IntegerExceptionTest { public static void main(String[] args) { try { throw new IntegerException(42); } catch (IntegerException e) { assert e.getValue() == 42; } } } 

TRy语句的主体抛出exception与一个给定的值,由catch子句捕获。

相比之下,新的exception的定义是禁止的,因为它创build了一个参数化的types:

 class ParametricException<T> extends Exception { // compile-time error private final T value; public ParametricException(T value) { this.value = value; } public T getValue() { return value; } } 

试图编译上述报告错误:

 % javac ParametricException.java ParametricException.java:1: a generic class may not extend java.lang.Throwable class ParametricException<T> extends Exception { // compile-time error ^ 1 error 

这种限制是明智的,因为几乎任何试图捕捉这种exception的尝试都必须失败,因为这种types是不可确定的。 人们可能会期望典型的例外使用如下所示:

 class ParametricExceptionTest { public static void main(String[] args) { try { throw new ParametricException<Integer>(42); } catch (ParametricException<Integer> e) { // compile-time error assert e.getValue()==42; } } } 

这是不允许的,因为catch子句中的types是不可确定的。 在撰写本文时,Sun编译器在这种情况下报告了一系列语法错误:

 % javac ParametricExceptionTest.java ParametricExceptionTest.java:5: <identifier> expected } catch (ParametricException<Integer> e) { ^ ParametricExceptionTest.java:8: ')' expected } ^ ParametricExceptionTest.java:9: '}' expected } ^ 3 errors 

由于exception不能是参数,所以语法是受限制的,因此必须将该types写为标识符,而没有以下参数。

这基本上是因为它的devise不好。

这个问题可以防止干净的抽象devise,

 public interface Repository<ID, E extends Entity<ID>> { E getById(ID id) throws EntityNotFoundException<E, ID>; } 

generics条款对generics不合格的事实没有被certificate是没有借口的。 编译器可以简单地禁止扩展Throwable的genericstypes,或者在catch子句中不允许使用generics。

我希望这是因为没有办法保证参数化。 考虑下面的代码:

 try { doSomethingThatCanThrow(); } catch (MyException<Foo> e) { // handle it } 

如你所说,参数化只是语法糖。 但是,编译器试图确保在编译范围内的对象的所有引用中参数化保持一致。 在发生exception的情况下,编译器无法保证MyException仅从正在处理的作用域中抛出。

generics在编译时检查types是否正确。 genericstypes信息然后在称为types擦除的进程中被删除 。 例如, List<Integer>将被转换为非genericsList

由于types擦除 ,types参数不能在运行时确定。

让我们假设你可以像这样扩展Throwable

 public class GenericException<T> extends Throwable 

现在让我们考虑下面的代码:

 try { throw new GenericException<Integer>(); } catch(GenericException<Integer> e) { System.err.println("Integer"); } catch(GenericException<String> e) { System.err.println("String"); } 

由于types擦除 ,运行时将不知道要执行哪个catch块。

因此,如果generics类是Throwable的直接或间接子类,那么这是一个编译时错误。

来源: types删除的问题