我有这样的代码:
class A {
public void func(String str, List<String> lst) {}
}
class B extends A {
public void func(String str, List<Integer> lst) {} // Does not compile
}
带有注释的那行不能编译,解释如下:"两个方法都有相同的擦除,但都不覆盖另一个方法"。一方面,我理解它没有重写,因为第二个参数中的泛型类型是不同的。另一方面,在擦除之后,两个函数本质上具有相同的签名,因此也不会重载。但我正在努力理解为什么它不编译。如有任何帮助,不胜感激。
添加:即使我将签名更改为
public Object func(String str, List<Integer> lst) {return null;}
由于同样的原因,仍然不能编译。现在这些是完全不同的签名,所以这让我更困惑了。
From JLS 8.4.2:
两个方法或构造函数M和N如果具有相同的名称,相同的类型参数,则具有相同的签名(如果有的话)(§8.4.4),调整后的正式参数类型的类型参数N M,相同的形式参数类型.
这和你的代码有什么关系?
-
您发布的第一个方法签名是:
public void func(String str, List<String> lst)
这个签名只在编译时存在,因为它使用泛型。编译后,由于类型擦除,运行时签名将如下所示(
List<String>
变为List
):public void func(String str, List lst)
-
你的例子中的第二个方法签名是:
public void func(String str, List<Integer> lst)
虽然起始点是不同的——一个整数列表而不是字符串列表——运行时签名也看到了类似的变化,将
List<Integer>
替换为List
:public void func(String str, List lst)
即使代码指定不同的参数列表(List<String>
vsList<Integer>
)让你相信有两种不同的方法签名,两个签名实际上是相同的:
- 它们都被命名为"func"和
- 它们有相同的参数列表:String, list
在泛型擦除中,他们展示了一个类似于您所看到的Node<T>
类型变为Node
的示例。这也发生在你的例子中,List<String>
变成了List
。
在类型擦除过程中,Java编译器擦除所有类型参数,如果类型参数有界,则将每个类型参数替换为其第一个绑定,如果类型参数没有界,则将每个类型参数替换为Object。
请注意,返回类型不是方法签名惟一性的一部分,这就是为什么将其更改为返回Object
并没有什么区别。
下面是Java教程中关于类型擦除的更多信息,在几个地方添加了一点重点:
泛型被引入到Java语言中,以便在编译时提供更严格的类型检查并支持泛型编程。为了实现泛型,Java编译器将类型擦除应用于:
- 将泛型类型中的所有类型参数替换为它们的边界,如果类型参数没有边界,则替换为Object。因此,生成的字节码只包含普通类、接口和方法。
- 插入类型强制转换以保证类型安全。
- 生成桥接方法以保持扩展泛型类型中的多态性。类型擦除确保不为参数化类型创建新类;因此,泛型不会产生运行时开销。