簡體   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