简体   繁体   English

Java中覆盖通用集合时出错

[英]error in overriding generic collections in Java

When I try to override a method that takes a List<String> , I get the following compile error . 当我尝试覆盖采用List<String>的方法时,出现以下编译错误

Multiple markers at this line: 这行有多个标记:
- The method getname(List<Integer>) of type child must override or implement a supertype method -该方法getname(List<Integer>)类型的child必须重写或实现的超类型方法
- Name clash: The method getname(List<Integer>) of type child has the same erasure as getname(List<String>) of type parent but does not override it -名称冲突:该方法getname(List<Integer>)类型的child具有相同的擦除作为getname(List<String>)型的parent ,但不覆盖它

I was under the impression, that due to erasure , a method that takes a List<String> and a subclass method that takes a List<Integer> would be considered as overridden , because the signature of both methods are same after erasure. 我的印象是,由于擦除 ,采用List<String>的方法和采用List<Integer>的子类方法将被视为已重写 ,因为擦除后这两种方法的签名相同。

Here is the definition for method signature that involves erasure . 是涉及擦除的 方法签名的定义。
I do not understand why this error comes and what exactly it means. 我不明白为什么会出现此错误以及它的确切含义。

My code is below: 我的代码如下:

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> and List<Integer> are different types and getname(List<String> num) and getname(List<Integer> num) are methods with different signatures. List<String>List<Integer>是不同的类型, getname(List<String> num)getname(List<Integer> num)是具有不同签名的方法。 So the second doesn't override the first. 因此,第二个不会覆盖第一个。 So child can not extends parent whit this method. 所以child不能扩展parent这种方法。

The error message is pretty clear: it has the same erasure, but the types don't match, so it's not considered an override. 错误消息非常清楚:它具有相同的擦除,但是类型不匹配,因此不被视为替代。 A List<Integer> is not a List<String> ; List<Integer>不是List<String> ; it can't treat it as either an override (which would require the types to match exactly) nor an overload (which would require the erasures to be different). 它既不能将其视为替代(将要求类型完全匹配),也不能将其视为重载(将擦除要求不同)。

Basically your impression is incorrect and this is impossible. 基本上您的印象是不正确的,这是不可能的。 The JLS considers this specifically illegal. JLS认为这特别非法。

From 8.4.2 : 8.4.2开始

The signature of a method m1 is a subsignature of the signature of a method m2 if either: 如果满足以下任一条件,则方法m1的签名是方法m2的签名的子签名:

  • m2 has the same signature as m1, or m2与m1具有相同的签名,或者

  • the signature of m1 is the same as the erasure (§4.6) of the signature of m2. m1 的签名与m2的签名的擦除(第4.6节)相同

Two method signatures m1 and m2 are override-equivalent iff either m1 is a subsignature of m2 or m2 is a subsignature of m1. 如果m1是m2的子签名或m2是m1的子签名,则两个方法签名m1和m2是等效的。

The emboldened bit is important because it doesn't say "the erasure of m1 is the same as the erasure of m2". 大胆的位很重要,因为它没有说“ m1的擦除与m2的擦除相同”。 What it actually does allow is this (and some more convoluted examples like it): 它实际上允许的是这样(以及一些更复杂的示例):

class A {
    void process(List<String> list) {}
}
class B extends A {
    @Override
    void process(List list) {} // |List| is erasure of List<T>
}

Since the method signature of B.process is the erasure of A.process it is an override. 由于B.process的方法签名是对B.process的擦除, A.process它是一个替代。

According to 8.4.9 , an example like in the OP could then be an overload because the signatures are not override-equivalent: 根据8.4.9 ,OP中的示例可能会过载,因为签名不是等效的:

If two methods of a class ... have the same name but signatures that are not override-equivalent, then the method name is said to be overloaded. 如果类的两个方法具有相同的名称,但签名不是等效的,则称该方法名称已重载。

Except that it's specifically a compile-time error ( 8.4.8.3 ): 除了特别是编译时错误( 8.4.8.3 ):

It is a compile-time error if a type declaration T has a member method m1 and there exists a method m2 declared in T or a supertype of T such that all of the following conditions hold: 如果类型声明T具有成员方法m1并且存在在T中声明的方法m2或T的超类型,从而满足以下所有条件,则是编译时错误:

  • m1 and m2 have the same name. m1和m2具有相同的名称。

  • m2 is accessible from T. 可从T访问m2。

  • The signature of m1 is not a subsignature (§8.4.2) of the signature of m2. m1的签名不是m2的签名的子签名(第8.4.2节)。

  • The signature of m1 or some method m1 overrides (directly or indirectly) has the same erasure as the signature of m2 or some method m2 overrides (directly or indirectly). m1或某些方法m1覆盖(直接或间接)的签名具有与m2或某些方法m2覆盖(直接或间接)的签名相同的擦除。

These restrictions are necessary because generics are implemented via erasure. 这些限制是必需的,因为泛型是通过擦除实现的。 The rule above implies that methods declared in the same class with the same name must have different erasures. 上面的规则意味着在同一个类中使用相同名称声明的方法必须具有不同的擦除。 ... ...

To add to the answers already here, I want to comment about the signature of the methods is the same after erasure ... but the compiler checks the method type before erasure. 要在此处添加答案,我想对the signature of the methods is the same after erasure进行评论……但是编译器会在擦除前检查方法类型。

You could do something like creating a "different" Parent class, such as 您可以执行类似创建“不同的” Parent类的操作,例如

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

, compile it, use it to compile Child class, and then mix your original Parent.class and Child.class in the same JVM without issue, avoiding the compiler checks and using type erasure. ,进行编译,使用它来编译Child类,然后将原始的Parent.classChild.class混合在同一JVM中而不会出现问题,从而避免了编译器检查并使用了类型擦除。

But as long as the compiler notices doing something like that "in the same run", it will fail by the reasons explained by Ashot and Louis. 但是只要编译器注意到“在同一运行中”执行类似的操作,由于Ashot和Louis解释的原因,它将失败。

The error you have pointed out is obviously because of the @Override annotation. 您指出的错误显然是由于@Override注释。 When validating annotations (which I presume must happen at the very early stage of compiling process) the type erasure hasn't occurred. 验证批注时(我认为必须在编译过程的早期阶段进行),但没有发生类型擦除。 So the types List is not same as List and you get the warning. 因此,列表类型与列表类型不同,您会收到警告。 But even without that annotation I get an error 但是即使没有该注释,我仍然会出错

    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{`. 

This error clearly says that they have the same erasure but still neither over rides. 该错误清楚地表明,它们具有相同的擦除,但仍然不会超出游标。 So Java in principle doesn't allow it. 因此,Java原则上不允许这样做。 I can understand it will lead to many problems/confusions if allowed. 我可以理解,如果允许的话,它将导致许多问题/困惑。 For eg if someone makes a call new Child().get_name( new List<String> () ) , it can't be resolved to the Child method which will break the concept of over riding. 例如,如果有人调用new Child().get_name( new List<String> () ) ,则无法将其解析为Child方法,这将打破过度骑行的概念。 So it is correct that it is not allowed. 因此,正确的做法是不允许这样做。

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

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