覆盖Java中的generics集合时出错
当我尝试覆盖一个采用List
,我得到以下编译错误 。
此行有多个标记:
– 子类型的方法getname(List)
必须覆盖或实现超类型方法
– 名称冲突:类型为child
的方法getname(List)
与parent
类型的getname(List)
具有相同的擦除,但不会覆盖它
我的印象是,由于擦除 ,采用List
的方法和采用List
的子类方法将被视为被覆盖 ,因为擦除后两种方法的签名都相同。
以下是涉及擦除的 方法签名的定义。
我不明白为什么会出现这个错误,究竟是什么意思。
我的代码如下:
class parent { public void getname(List num) { System.out.printf("parent class: %s",num); } } class child extends parent { @Override // here is the compile error public void getname(List num) { System.out.printf("child class: %s",num); } }
List
和List
是不同的类型, getname(List
和getname(List
是具有不同签名的方法。 所以第二个不会覆盖第一个。 所以child
不能通过这种方法扩展parent
。
错误消息非常清楚:它具有相同的擦除,但类型不匹配,因此它不被视为覆盖。 List
不是List
; 它不能将它视为覆盖(这将要求类型完全匹配)或重载(这将要求删除不同)。
基本上你的印象是不正确的,这是不可能的。 JLS认为这是非法的。
从8.4.2开始 :
方法m1的签名是方法m2的签名的子签名,如果:
m2与m1具有相同的签名,或
m1 的签名与m2签名的擦除(§4.6)相同。
如果m1是m2的子签名或m2是m1的子签名,则两个方法签名m1和m2是覆盖等价的。
加粗的位很重要,因为它没有说“m1的擦除与m2的擦除相同”。 它实际上允许的是这个(以及一些更复杂的例子):
class A { void process(List list) {} } class B extends A { @Override void process(List list) {} // |List| is erasure of List }
由于B.process
的方法签名是B.process
的擦除, A.process
它是一个覆盖。
根据8.4.9 ,像OP中的例子可能是一个重载,因为签名不是覆盖等价的:
如果类的两个方法具有相同的名称但签名不是覆盖等效的,那么方法名称将被重载。
除了它特别是编译时错误( 8.4.8.3 ):
如果类型声明T具有成员方法m1并且存在以Tforms声明的方法m2或T的超类型以使得满足以下所有条件,则为编译时错误:
m1和m2具有相同的名称。
m2可从T访问。
m1的签名不是m2签名的子签名(第8.4.2节)。
m1的签名或某些方法m1覆盖(直接或间接)具有与m2的签名相同的擦除或某种方法m2覆盖(直接或间接)。
这些限制是必要的,因为generics是通过擦除来实现的。 上面的规则意味着在同一个类中声明的具有相同名称的方法必须具有不同的擦除。 …
要添加到这里的答案,我想评论the signature of the methods is the same after erasure
…但编译器在擦除之前检查方法类型。
您可以执行类似创建“不同” Parent
类的操作,例如
class Parent { public void getname(List num) { System.out.printf("child class: %s",num); } }
,编译它,用它来编译Child
类,然后将原始的Parent.class
和Child.class
混合在同一个JVM中,没有问题,避免了编译器检查和使用类型擦除。
但只要编译器注意到“在同一次运行中”做类似的事情,它就会因为Ashot和Louis解释的原因而失败。
您指出的错误显然是因为@Override注释。 validation注释(我认为必须在编译过程的早期阶段发生)时,类型擦除尚未发生。 因此List类型与List不同,您会收到警告。 但即使没有这个注释,我也会收到错误
name clash: getname(java.util.List) in child and getname(java.util.List) in parent have the same erasure, yet neither overrides the other class child extends parent{`.
这个错误明确表示他们有相同的擦除但仍然没有超过骑行。 所以Java原则上不允许它。 我可以理解,如果允许,它会导致许多问题/混淆。 例如,如果某人调用了new Child().get_name( new List
,则无法将其解析为将破坏过度骑行概念的Child方法。 所以不允许这是正确的。