我有一个像这样的简单程序:
package test;
public class TestGenericsInheritance {
public static void main(String[] args) {}
public static abstract class A<Q>{
public void foo2(Q obj){}
public abstract <T> void foo(T obj);
}
public static class C extends A<Object>{
@Override
public <T> void foo(T obj) {}
}
public static class B extends A{
@Override
public <T> void foo(T obj) {}
}
}
正如你所看到的那样绝对没有。在java 1.6和1.7上编译此程序文件,并出现以下错误:
/ d:/项目/.../测试/ TestGenericsInheritance.java:[24,19] test.TestGenericsInheritance.B不是抽象的,不会覆盖 抽象方法foo(java.lang.Object)in test.TestGenericsInheritance.A /D:/Projects/.../test/TestGenericsInheritance.java:[27,25]名称冲突: foo(T)in test.TestGenericsInheritance .B和foo(T)in test.TestGenericsInheritance.A具有相同的擦除,但两者都没有 覆盖另一个 /D:/Projects/.../test/TestGenericsInheritance.java:[26,9]方法做 不要从超类型
覆盖或实现方法
类C和B在语义上是相同的,但是类B不能将方法foo识别为A#foo的实现。为了使这个代码兼容,我必须在B类中使用以下签名实现方法A#foo:
public void foo(Object obj)
我的问题是,为什么我的程序无法编译?通用类型Q和T是完全独立的,那么为什么我只有在为继承的类A明确指定泛型类型Q时才能实现泛型函数A#foo?
由于
答案 0 :(得分:6)
B
扩展了原始类型A
。因为您使用原始类型,所以删除所有通用信息,而不仅仅是您未指定的类型变量(请参阅Section 4.6 of the Java Language Specification)。这意味着A
有方法void foo(Object obj)
,而A<SomeType>
有方法<T> void foo(T obj)
。
如果重写方法,它必须具有相同的签名(可选地在重写方法的类型擦除之后) - 有趣的是,重写方法可能具有不同的,更具体的返回类型。两种方法的签名是不同的(在覆盖方法中需要类型擦除),因此您的示例不会编译。
类型擦除原因实现的原因是向后兼容性。我们的想法是新代码只使用泛型,而只有旧代码才会使用原始类型。因此,目标不是使原始类型最方便(因为新代码不应该使用它们),而是使原始类型最兼容。
考虑一下类ArrayList
,它是在Java 2中引入的(在泛型之前)。它有一个方法public Object[] toArray(Object[] a)
。在Java 5中引入了泛型,并且可以毫不费力地将此方法的签名更改为public <T> T[] toArray(T[] a)
(其中T
不是元素类型):因为实现了类型擦除的方式,对于旧代码使用(或子类)ArrayList
而不是ArrayList<SomeType>
方法签名保持不变。