我今天碰到了这个,我唯一能想到的是这是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
没有参与这些方法......?
有谁知道这里发生了什么?
答案 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东西都使得泛型类信息成为目标文件中的一等公民。
所以无论如何,这些家伙显然比我更了解你的问题,但是你应该考虑的是所有真正发生的事情都是编译器漏洞。