根据Java语言规范,第3版:
如果泛型类是其直接或间接子类,则为编译时错误
Throwable
.
我希望理解为什么做出这个决定.通用异常有什么问题?
(据我所知,泛型只是编译时的语法糖,他们将被翻译成Object
反正在.class
文件,因此有效地声明一个泛型类是仿佛一切都在它是一个Object
,请纠正我,如果我错了.)
正如mark所说,这些类型是不可恢复的,这在以下情况下是一个问题:
try { doSomeStuff(); } catch (SomeExceptione) { // ignore that } catch (SomeException e) { crashAndBurn() }
既SomeException
和SomeException
被擦除至相同类型的,也没有办法为JVM区分异常情况,因此没有办法知道哪些catch
应该被执行的块.
以下是如何使用异常的简单示例:
class IntegerExceptionTest { public static void main(String[] args) { try { throw new IntegerException(42); } catch (IntegerException e) { assert e.getValue() == 42; } } }
TRy语句的主体抛出具有给定值的异常,该值由catch子句捕获.
相反,禁止使用以下新异常定义,因为它创建了一个参数化类型:
class ParametricExceptionextends 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 ParametricExceptionextends Exception { // compile-time error ^ 1 error
这种限制是明智的,因为几乎任何捕获此类异常的尝试都必须失败,因为该类型不可恢复.有人可能会认为异常的典型用法如下:
class ParametricExceptionTest { public static void main(String[] args) { try { throw new ParametricException(42); } catch (ParametricException e) { // compile-time error assert e.getValue()==42; } } }
这是不允许的,因为catch子句中的类型不可恢复.在撰写本文时,Sun编译器在这种情况下报告了一连串的语法错误:
% javac ParametricExceptionTest.java ParametricExceptionTest.java:5:expected } catch (ParametricException e) { ^ ParametricExceptionTest.java:8: ')' expected } ^ ParametricExceptionTest.java:9: '}' expected } ^ 3 errors
由于异常不能参数化,因此语法受到限制,因此必须将类型写为标识符,而不包含以下参数.
这主要是因为它的设计方式很糟糕.
此问题可防止干净的抽象设计,例如
public interface Repository> { E getById(ID id) throws EntityNotFoundException ; }
一个catch子句因泛型而失败的事实并没有被证实是没有任何借口的.编译器可以简单地禁止在catch子句中扩展Throwable或禁止泛型的具体泛型类型.