2017-02-26 35 views
2

在重寫時,如果只涉及raw/unerased類型,則不需要額外的類型變量來縮小超類方法的返回類型。例如:Narrowing return types on inheritance (generics involved)使用泛型的奇怪編譯器行爲:縮小繼承的返回類型參數

但是,如果類型參數變窄 - 而原始類型保持不變 - 則需要一個附加的方法類型變量。

例子:

import java.util.List; 

public class InheritanceTest { 

    public interface A<T> { 

     A<T> test(); 

     List<A<T>> testList1(); 

     <U extends A<T>> List<U> testList2(); 

     <U> List<A<T>> testList3(); 

     // extending class/interface does not need to suppress warnings: 
     List<? extends A<T>> testList4(); 
    } 

    public interface B extends A<Integer> { 

     @Override 
     B test(); 

     @Override 
     List<B> testList1(); 

     @Override @SuppressWarnings("unchecked") 
     List<B> testList2(); 

     @Override @SuppressWarnings("unchecked") 
     List<B> testList3(); 

     @Override 
     List<B> testList4(); 
    } 
} 

編譯器抱怨不兼容的返回類型爲testList1(),但其餘三種方法不報告任何錯誤。 `

testList3()中未使用的變量如何使編譯器關閉?

+1

請發表[mcve] –

+0

謝謝。我簡化了這個例子。 – Andi

+1

「如果兩個方法或構造函數M和N具有相同的名稱,相同的類型參數(如果有)(§8.4.4),並且在將N的形式參數類型修改爲M的類型參數,相同的形式參數類型。「如果您沒有相同的類型參數,則它們不是覆蓋等效的。 –

回答

0

在原始類型和泛型混合的情況下,語言規範放棄。否則會引入更加晦澀的規則,所有這些都支持應該避免的情況。

A.testList3是一種通用方法。 B嘗試聲明與非泛型相同的方法(與參數和返回值相同的名稱,相同的擦除類型)。所以,語言規範似乎忽略了所有的方法都是兼容的。

(所以要謹慎,不要壓抑,警告。)

+0

這並不回答我關於'A.testList3'的問題,因爲你對'A.testList1'也是這樣說的:在這裏,問題只能通過將行更改爲'List <?擴展A > testList1();'。 – Andi

1

當你覆蓋了協變返回類型的方法,你基本上說,被覆蓋的返回類型是超級回報的一個子類類型。含義List<B>可分配給List<A<T>>,這是不正確的。這裏是不使用你的類型的方法聲明一個簡單的例子(A<T>B;他們有什麼方法並不重要這裏):

List<A<Integer>> listOfA; 
List<B> listOfB = new ArrayList<>(); 
listOfA = listOfB; // compile error: incompatible types: List<B> cannot be converted to List<A<Integer>> 

List<? extends A<Integer>> listOfAFixed; 
listOfAFixed = listOfB; 

...爲什麼這個任務是不允許的,因爲有可能是原因一個C extends A<Integer>這將引入潛在的一個ClassCastException

List<C> listOfC = new ArrayList<>(); 
listOfA = listOfC; // let's assume this compiles 
B b = getAB(); 
listOfA.add(b); // adding to listOfC, right? 
C c = listOfC.get(0); // whops, ClassCastException 

可以用Number取代A<T>BIntegerCFloat;同樣的事情會發生。 問題不在於A上的仿製藥,而是List的仿製藥。

使用相同的邏輯,我試圖重現方法體中的其他協變覆蓋(〜賦值),但它們並沒有給出警告,而是出現了錯誤。這很古怪。實現

一件事是,從A去除參數仍然給出了同樣的警告

public interface A { 
    <U extends A> List<U> testList2(); 
    <U> List<A> testList3(); 
} 

public interface B extends A { 
    @Override List<B> testList2(); 
    @Override List<B> testList3(); 
} 

我認爲訣竅在於一個事實,即首要方法不是「通用不夠」(缺更好的術語,我知道)的,看看列出的內部簽名通過javap

<U::LInheritanceTest$A;>()Ljava/util/List<TU;>; 
()Ljava/util/List<LInheritanceTest$B;>; 

<U:Ljava/lang/Object;>()Ljava/util/List<LInheritanceTest$A;>; 
()Ljava/util/List<LInheritanceTest$B;>; 

注意,壓倒一切的方法缺乏<>,WHI我猜是類似於使用原始類型,因此未經檢查的警告,而不是錯誤:它使用部分擦除類型。

這是我放棄的一點,也許安迪或Jorn可以使這個更正式解釋。