2 回答

TA貢獻1876條經驗 獲得超6個贊
要理解這些問題,您必須了解泛型是如何工作的subtyping(在 Java 中使用關鍵字明確表示extends)。Andreas 提到了PECS規則,這是它們在 Java 中的表示。
首先,我想指出上面的代碼可以通過簡單的強制轉換來糾正
ArrayList<? super Student> list = new ArrayList<>();
list.add(new Student());
ArrayList<Person> a = (ArrayList<Person>) list; // a covariance
a.add(new Person());
并且編譯和運行良好(而不是引發任何異常)
原因很簡單,當我們有一個consumer(接受一些對象并使用它們,例如方法add)時,我們希望它接受我們指定類型no more than(超類)的對象T,因為使用過程可能需要任何成員(變量,方法等)它想要的類型,我們希望確保該類型T滿足消費者所需的所有成員。
相反,producer為我們生成對象(如get方法)的 a 必須提供no less than指定類型的對象T,以便我們可以訪問T生成的對象上的任何成員。
covariance這兩個與稱為和的子類型形式密切相關contravariance
至于第二個問題,你也可以參考一下的實現Consumer<T>(比較簡單):
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
我們需要這個的原因? super T是:當我們使用Consumer方法組合兩個 s時andThen,假設前者Consumer采用 type 的對象T,我們希望后者采用 type 的對象no more than T,這樣它就不會嘗試訪問任何T不有。
因此,我們不是簡單地寫Consumer<T> afterbut Consumer<? super T> after,而是允許前一個消費者(類型T)與一個消費者結合,該消費者接受一個不完全是 type 的對象T,但可能比 小T,方便covariance。這使得以下代碼聽起來:
Consumer<Student> stu = (student) -> {};
Consumer<Person> per = (person) -> {};
stu.andThen(per);
出于同樣的考慮,compose類型方法也適用。Function

TA貢獻1824條經驗 獲得超5個贊
IMO 這可能是 vanilla Java 中最復雜的概念。所以讓我們把它分解一下。我將從你的第二個問題開始。
Function<T, R>t獲取type 的實例并返回type 的T實例。通過繼承,這意味著您可以提供if類型的實例,并類似地返回if類型。rRfooFooFoo extends TbarBarBar extends R
作為一個想要編寫一個靈活的泛型方法的庫維護者,很難,實際上不可能提前知道所有可能與該方法一起使用的擴展T和R. 那么我們如何編寫處理它們的方法呢?此外,這些實例具有擴展基類的類型這一事實與我們無關。
這就是通配符的用武之地。在方法調用期間,我們說您可以使用滿足所需類范圍的任何類。對于所討論的方法,我們有兩個不同的通配符,它們使用上限和下限泛型類型參數:
public interface Function<T, R>{
default <V> Function<V, R> compose(Function<? super V, ? extends T> before)
現在讓我們說我們想利用這個方法......例如讓我們定義一些基本類:
class Animal{}
class Dog extends Animal{}
class Fruit{}
class Apple extends Fruit{}
class Fish{}
class Tuna extends Fish{}
想象一下我們的函數和轉換定義如下:
Function<Animal, Apple> base = ...;
Function<Fish, Animal> transformation = ...;
我們可以組合這些函數compose來創建一個新函數:
Function<Fish, Apple> composed = base.compose(transformation);
這一切都很好,但現在想象一下,在所需的輸出函數中,我們實際上只想用作Tuna輸入。如果我們不使用下界作為我們傳遞給的? super V輸入類型參數,那么我們會得到一個編譯器錯誤:Functioncompose
default <V> Function<V, R> compose(Function<V, ? extends T> before)
...
Function<Tuna, Apple> composed = base.compose(transformation);
> Incompatible types:
> Found: Function<Fish, Apple>, required: Function<Tuna, Apple>
發生這種情況是因為調用的返回類型compose指定V為,Tuna而transformation另一方面指定其“ V”為Fish。所以現在當我們嘗試傳遞transformation給compose編譯器時需要transformation接受 aTuna作為其V當然Tuna不完全匹配Fish。
另一方面,代碼的原始版本 ( ? super V) 允許我們將其視為V下界(即它允許“逆變”與“不變” V)。編譯器不會遇到Tuna和之間的不匹配,而是Fish能夠成功應用? super V計算結果為 的下限檢查Fish super Tuna,自 以來為真Tuna extends Fish。
對于另一種情況,假設我們的調用定義為:
Function<Animal, Apple> base = ...;
Function<Fish, Dog> transformation = ...;
Function<Fish, Apple> composed = base.compose(transformation);
如果我們沒有通配符,? extends T那么我們會得到另一個錯誤:
default <V> Function<V, R> compose(Function<? super V, T> before)
Function<Fish, Apple> composed = base.compose(transformation);
// error converting transformation from
// Function<Fish, Dog> to Function<Fish, Animal>
通配符允許它按照is resolved to 的? extends T方式工作,并且通配符 resolves to可以滿足約束條件。TAnimalDogDog extends Animal
對于你的第一個問題;這些邊界實際上只在方法調用的上下文中起作用。在該方法的過程中,通配符將被解析為實際類型,就像? super V被解析為Fish和? extends T被解析為一樣Dog。如果沒有來自泛型簽名的信息,我們將無法讓編譯器知道可以在類型的方法上使用哪個類,因此不允許使用任何類。
添加回答
舉報