添加Generic允许您覆盖具有不同返回类型的方法?

时间:2011-03-28 17:05:34

标签: java generics

我今天碰到了这个,我唯一能想到的是这是Java编译器中的一个错误。下面的代码编译,但肯定看起来不正确(因为testMethod在子节点中有一个differenet签名但覆盖了父节点)并且会在运行时抛出类转换异常。

public interface TestInterface<T> {
  public List<String> testMethod(); // <-- List<String>
}
public class TestClass implements TestInterface {
  @Override
  public List<Integer> testMethod() { // <-- List<Integer> overriding List<String>!!
      return Collections.singletonList(1);
  }
}

使用上述结构:

public void test() {
  TestInterface<Boolean> test = new TestClass();
  List<String> strings = test.testMethod();
  for (String s : strings) {
    System.out.println(s);
  }
}

所有这些编译都很好,但是如果你运行它会明显抛出类别转换异常。

如果您从<T>移除TestInterface,或在T行填写TestClass implements TestInterface<T>,则代码将不再编译,这是有道理的。 Imo <T>应该对testMethod的编译没有影响,因为它不参与该方法。  也许将<T>添加到TestInterface会导致编译器键入 - 擦除方法签名,即使T没有参与这些方法......?

有谁知道这里发生了什么?

2 个答案:

答案 0 :(得分:9)

如果将泛型类实例化为原始类型,则编译器将忽略其中包含的所有泛型类型参数,因此在编译期间不会给出警告/错误。即声明

public class TestClass implements TestInterface ...

有效地将代码降级为

public interface TestInterface {
  public List testMethod();
}
public class TestClass implements TestInterface {
  @Override
  public List testMethod() {
      return Collections.singletonList(1);
  }
}

确实很好。

几个星期前发布了一个类似的问题,the answer to which声明它不是编译器错误,而是为了向后兼容而进行的深思熟虑的设计决策。

答案 1 :(得分:0)

我理解它的方式是泛型是编译器方面的事情。类文件中没有包含通用类型信息,它就像编译器黑客一样。这就是为什么你可以轻松地将通用负载调用与无泛型类混合,并且在运行时没有问题。 我听说所有的dotnet东西都使得泛型类信息成为目标文件中的一等公民。

所以无论如何,这些家伙显然比我更了解你的问题,但是你应该考虑的是所有真正发生的事情都是编译器漏洞。