为什么Java不支持通用Throwables?

class Bouncy extends Throwable { } // Error: the generic class Bouncy may not subclass java.lang.Throwable 

为什么Java不支持通用的Throwable

我意识到类型擦除使某些事情变得复杂,但显然Java已经经历了很多,所以为什么不再推动它,并允许genericsThrowable s,通过全面的编译时检查潜在的问题?


我觉得类型擦除的论点相当薄弱。 目前,我们做不到:

 void process(List list) { } void process(List list) { } 

当然,我们没有它。 我不是要求我们能够在同一个try块中catch BouncyBouncy ,但是如果我们在具有严格编译时可执行规则的不相关上下文中使用它们(这几乎就是这样的话)仿制药现在正常工作),它不可行吗?

Java语言规范8.1.2通用类和类型参数 :

由于Java虚拟机的catch机制仅适用于非generics类,因此需要此限制。

就个人而言,我认为这是因为我们无法在catch子句中获得generics的任何好处。 由于类型擦除,我们不能写catch (Bouncy ex) ,但是如果我们写catch (Bouncy ex) ,那么使它成为通用将是无用的。

键入擦除。 运行时exception类型没有generics信息。 因此你做不到

 } catch( Mistake ea) { ... } catch( Mistake eu) { ... } 

所能做的就是

 catch( Mistake ea ) { ... } 

类型擦除是当Java从1.4移动到1.5时决定保持向后兼容性的方式。 许多人当时并不高兴,这是理所当然的。 但考虑到部署代码的数量,打破1.4中愉快工作的代码是不可想象的。

简短回答:因为他们采取捷径,就像他们擦除一样。

答案很长:正如其他人已经指出的那样,由于擦除,在“catch MyException ”和“catch MyException ”之间无法在运行时产生差异。

但这并不意味着不需要通用exception 。 我希望generics能够使用通用字段! 他们可以简单地允许genericsexception,但只允许在原始状态下捕获它们(例如“catch MyException”)。

当然,这会使仿制药变得更加复杂。 这是为了表明擦除generics的决定有多糟糕。 我们什么时候才能拥有支持真正的generics(带RTTI)的Java版本,而不是当前的语法糖?

您仍然可以使用generics方法,如下所示:

 public class SomeException { private final Object target; public SomeException(Object target) { this.target = target; } public  T getTarget() { return (T) target; } } .... catch (SomeException e) { Integer target = e.getTarget(); } 

我同意克里斯蒂安的上述答案。 虽然接受的答案在技术上是正确的(只要它引用了JVM规范),但Cristian Vasile的答案是有资格甚至挑战限制的答案。

在这个问题的答案中我注意到至少有两个论点我不同意并且我会反驳。 如果这些答案中的参数是正确的,我们可以使用这些参数来攻击今天成功使用它们的其他上下文中的generics。


第一个参数声明我们不能使用它:

 catch (Exception e) {} 

因为JVM不知道如何使用Exception 。 在JVM不知道如何使用List的基础上,这个论点似乎也会攻击generics的使用:

 List list; 

当然,这个论点会忘记编译器执行类型擦除,因此JVM不需要知道如何处理Exception 。 它可以简单地处理Exception ,就像处理List

当然,由于类型擦除,我们永远不会在同一个try / catch中处理catch(Exception e)catch(Exception e) ,但是再次,这并不比使用方法参数或返回值更糟糕:我们今天也没有处理myMethod(List)myMethod(List) …(我在下面的第二个反驳中重申了这个方面。)


第二个论点如下。 我们不允许这样:

 catch (Exception e) {} 

因为这不起作用:

 catch (Exception e) {} catch (Exception e) {} 

好的,那么为什么不禁止这个:

 interface MyInterface { Comparable getComparable(); } 

因为这不起作用

 interface MyInterface { Comparable getComparable(); Comparable getComparable(); } 

或这个:

 interface MyInterface { void setComparable(Comparable comparable); } 

因为这不起作用

 interface MyInterface { void setComparable(Comparable comparable); void setComparable(Comparable comparable); } 

换句话说,为什么不在大多数情况下禁止generics呢?

这第二个论点忘记了,虽然我们不可能允许在这些上下文中擦除相同的非generics构造的不同generics构造,但是只要类型不擦除 ,我们仍然可以做下一个最好的事情并允许generics到同一类型 。 这就是我们用方法参数做的事情:我们允许你使用generics,但是一旦我们在类型擦除后检测到重复签名就会抱怨。 好吧,我们可以用exception和catch块做同样的事情……


最后,我将扩展Cristian的答案。 而不是允许genericsexception类catch块中使用原始类型:

 class MyException {} ... catch (MyException e) { // raw 

Java本可以完全没有问题:

 class MyException {} ... catch (MyException e) { 

可以执行以下操作:

  1. Throwables可以实现通用接口,只要throwable本身没有类型参数,例如

interface Bouncy {
// ...
}
class BouncyString extends Exception implements Bouncy {
// ...
}

  • throws子句可以引用类型参数,例如

    static void
    throwIfInstanceOf(Throwable ex, Class clazz) throws X {
    if (clazz.isInstance(ex)) throw clazz.cast(ex);
    }