繁体   English   中英

Java中覆盖通用集合时出错

[英]error in overriding generic collections in Java

当我尝试覆盖采用List<String>的方法时,出现以下编译错误

这行有多个标记:
-该方法getname(List<Integer>)类型的child必须重写或实现的超类型方法
-名称冲突:该方法getname(List<Integer>)类型的child具有相同的擦除作为getname(List<String>)型的parent ,但不覆盖它

我的印象是,由于擦除 ,采用List<String>的方法和采用List<Integer>的子类方法将被视为已重写 ,因为擦除后这两种方法的签名相同。

是涉及擦除的 方法签名的定义。
我不明白为什么会出现此错误以及它的确切含义。

我的代码如下:

class parent {

     public void getname(List<String> num) {
        System.out.printf("parent class: %s",num);
     }

}

class child extends parent {

    @Override  // here is the compile error
    public void getname(List<Integer> num) {
        System.out.printf("child class: %s",num);
    }
}

List<String>List<Integer>是不同的类型, getname(List<String> num)getname(List<Integer> num)是具有不同签名的方法。 因此,第二个不会覆盖第一个。 所以child不能扩展parent这种方法。

错误消息非常清楚:它具有相同的擦除,但是类型不匹配,因此不被视为替代。 List<Integer>不是List<String> ; 它既不能将其视为替代(将要求类型完全匹配),也不能将其视为重载(将擦除要求不同)。

基本上您的印象是不正确的,这是不可能的。 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<String> list) {}
}
class B extends A {
    @Override
    void process(List list) {} // |List| is erasure of List<T>
}

由于B.process的方法签名是对B.process的擦除, A.process它是一个替代。

根据8.4.9 ,OP中的示例可能会过载,因为签名不是等效的:

如果类的两个方法具有相同的名称,但签名不是等效的,则称该方法名称已重载。

除了特别是编译时错误( 8.4.8.3 ):

如果类型声明T具有成员方法m1并且存在在T中声明的方法m2或T的超类型,从而满足以下所有条件,则是编译时错误:

  • m1和m2具有相同的名称。

  • 可从T访问m2。

  • m1的签名不是m2的签名的子签名(第8.4.2节)。

  • m1或某些方法m1覆盖(直接或间接)的签名具有与m2或某些方法m2覆盖(直接或间接)的签名相同的擦除。

这些限制是必需的,因为泛型是通过擦除实现的。 上面的规则意味着在同一个类中使用相同名称声明的方法必须具有不同的擦除。 ...

要在此处添加答案,我想对the signature of the methods is the same after erasure进行评论……但是编译器会在擦除前检查方法类型。

您可以执行类似创建“不同的” Parent类的操作,例如

class Parent {
  public void getname(List<Integer> num) {
    System.out.printf("child class: %s",num);
  }
}

,进行编译,使用它来编译Child类,然后将原始的Parent.classChild.class混合在同一JVM中而不会出现问题,从而避免了编译器检查并使用了类型擦除。

但是只要编译器注意到“在同一运行中”执行类似的操作,由于Ashot和Louis解释的原因,它将失败。

您指出的错误显然是由于@Override注释。 验证批注时(我认为必须在编译过程的早期阶段进行),但没有发生类型擦除。 因此,列表类型与列表类型不同,您会收到警告。 但是即使没有该注释,我仍然会出错

    name clash: getname(java.util.List<java.lang.Integer>) in child and getname(java.util.List<java.lang.String>) in parent have the    same erasure, yet neither overrides the other
class child extends parent{`. 

该错误清楚地表明,它们具有相同的擦除,但仍然不会超出游标。 因此,Java原则上不允许这样做。 我可以理解,如果允许的话,它将导致许多问题/困惑。 例如,如果有人调用new Child().get_name( new List<String> () ) ,则无法将其解析为Child方法,这将打破过度骑行的概念。 因此,正确的做法是不允许这样做。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM