2017-10-29 51 views
5
public class Base { 

    <T> List<? extends Number> f1() {return null;} 
    List<? extends Number>  f2() {return null;} 
    <T extends Number> List<T> f3() {return null; } 
} 

class Derived extends Base { 

    List<String> f1() {return null;} // compiles fine !!! 
    List<String> f3() {return null; } // compiles fine !!! 

    // compile ERR: return type is incompatible with Base.f2() 
    List<String> f2() {return null;} 
} 

爲什麼覆蓋方法F1()和派生類F3()的定義,得到沒有編譯錯誤,如在派生類(其給出編譯錯誤「返回類型重寫F2()方法的定義與基本不相容。 f2()「)?當使用非泛型方法重寫泛型方法時,爲什麼副標記和未檢查的規則在返回類型上以這種方式工作?

JLS中的子簽名覆蓋規則允許覆蓋方法(在Derived類中)是非泛型的,而覆蓋方法(在Base類中)是泛型的。

未經檢查的覆蓋規則允許在子類List<String>中生成返回類型,而不是基類中的List<T>

但我無法解釋下面的行爲差異,我不明白爲什麼f1()和f3()覆蓋Derived類中的定義已成功編譯(在Eclipse,SE8上),忽略了有界類型參數f3()和有界通配符f1()

P.S.我的猜測 - 在Derived編譯器中的f1()和f3()中,將這兩種方法視爲僅返回「raw」。List - 編譯器首先進行擦除(此時僅在Derived !?中),然後將Derived中的這些擦除方法與未擦除(到目前爲止)在Base中的方法。現在未檢查覆蓋規則是可以的(並且不需要檢查邊界 - 它根本不可能),編譯器決定這是正確的覆蓋並且編譯進一步...以及Base.f1()和Base中編譯泛型末尾的某處.f3()也刪除:)))

This SO answer也增加了這個主題的想法。

回答

3

您的f1f3覆蓋不是通用的,儘管最初的聲明是通用的。編譯允許覆蓋的返回類型與原始返回類型不同,因爲它們具有相同類型的擦除(List)。我認爲JLS 8.4.5後面,雖然坦率地說,我覺得這部分規範有點混淆。

如果你改變了覆蓋再次成爲通用:

<T> List<String> f1() {return null;} 
<T extends Number> List<String> f3() {return null; } 

...那麼這兩個無法編譯:

error: <T#1>f1() in Derived cannot override <T#2>f1() in Base 
    <T> List<String> f1() {return null;} 
        ^
    return type List<String> is not compatible with List<? extends Number> 
    where T#1,T#2 are type-variables: 
    T#1 extends Object declared in method <T#1>f1() 
    T#2 extends Object declared in method <T#2>f1() 

error: <T#1>f3() in Derived cannot override <T#2>f3() in Base 
    <T extends Number> List<String> f3() {return null; } 
            ^
    return type List<String> is not compatible with List<T#1> 
    where T#1,T#2 are type-variables: 
    T#1 extends Number declared in method <T#1>f3() 
    T#2 extends Number declared in method <T#2>f3() 

注意,即使在你的原代碼,javac發出警告左右不安全的轉換,如果您使用-Xlint:unchecked它提供了詳細信息:

return type requires unchecked conversion from List<String> to List<? extends Number> 
return type requires unchecked conversion from List<String> to List<T>