在异常“throws”中使用泛型并且具有List <string>参数的类不能编译

时间:2016-05-09 16:52:14

标签: java generics

我不明白为什么classB不编译:

interface Interface {
  <T extends Exception>void method(List<String> list) throws T;
}

// It compile but there is a warning (I understand why)
class ClassA implements Interface {
  public void method(List list) throws Exception{}
}

// This class don't compile (I don't get why)
class ClassB implements Interface {
  public void method(List<String> list) throws Exception{}
}

ClassB中的编译器错误消息:

  

ClassB类型的方法method_1(List)具有相同的擦除   作为Interface类型的method_1(List)但不覆盖   它

或许问题是ClassA编译的原因。

为什么&lt;字符串&gt;有所作为?

1 个答案:

答案 0 :(得分:4)

让我们首先介绍ClassB

方法的类型参数由方法的调用者解析。它不能通过子类中的覆盖来解决。

有两种方法可以使ClassB起作用。您也可以在那里添加类型参数。 1

class ClassB implements Interface {
    public <T extends Exception> void method(List<String> list) throws T{}
}

或者您可以将类型参数移动到接口,因此子类可以决定。

interface Interface<T extends Exception> {
    void method(List<String> list) throws T;
}
class ClassB implements Interface<Exception> {
    public void method(List<String> list) throws Exception {}
}

至于为什么ClassA有效,这是因为参数中的原始类型。当您使用原始类型时,所有变为原始类型,这意味着在编译ClassA时,Interface看起来像这种原始类型。

interface Interface {
    void method(List list) throws Exception;
}

这样做是为了向后兼容。见JLS 4.8 Raw Types

  

原始类型的超类(分别是超级接口)是其任何参数化调用的超类(超接口)的擦除。

接着说:

  

原始类型的使用仅允许作为遗留代码兼容性的让步。 强烈建议不要在将泛型引入Java编程语言后编写的代码中使用原始类型 。未来版本的Java编程语言可能会禁止使用原始类型。

1 我实际上想知道调用者如何解析T,即编译器如何推断T是什么,假设什么都没有在方法调用中将指示有效的异常。

根据Eclipse编译器,当您编写以下内容时,T将被解析为RuntimeException

new ClassB().method(null);

您可以通过明确指定类型来覆盖它,例如这将使方法抛出ParseException

new ClassB().<ParseException>method(null);

<强>后续

Java 8中推断的TRuntimeException。如果没有catch语句,我会在早期版本的Java上遇到以下错误:

1.5.0_22: unreported exception T; must be caught or declared to be thrown
1.6.0_45: unreported exception T; must be caught or declared to be thrown
1.7.0_79: error: unreported exception Exception; must be caught or declared to be thrown

Java 8将整个章节专门用于type inference,它说:

  

如果绑定集包含throwsα i ,并且α i 的正确上限最多为Exception,{ {1}}和Throwable,然后T i = Object