2 回答

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

TA貢獻(xiàn)1824條經(jīng)驗(yàn) 獲得超5個(gè)贊
IMO 這可能是 vanilla Java 中最復(fù)雜的概念。所以讓我們把它分解一下。我將從你的第二個(gè)問(wèn)題開(kāi)始。
Function<T, R>t獲取type 的實(shí)例并返回type 的T實(shí)例。通過(guò)繼承,這意味著您可以提供if類型的實(shí)例,并類似地返回if類型。rRfooFooFoo extends TbarBarBar extends R
作為一個(gè)想要編寫(xiě)一個(gè)靈活的泛型方法的庫(kù)維護(hù)者,很難,實(shí)際上不可能提前知道所有可能與該方法一起使用的擴(kuò)展T和R. 那么我們?nèi)绾尉帉?xiě)處理它們的方法呢?此外,這些實(shí)例具有擴(kuò)展基類的類型這一事實(shí)與我們無(wú)關(guān)。
這就是通配符的用武之地。在方法調(diào)用期間,我們說(shuō)您可以使用滿足所需類范圍的任何類。對(duì)于所討論的方法,我們有兩個(gè)不同的通配符,它們使用上限和下限泛型類型參數(shù):
public interface Function<T, R>{
default <V> Function<V, R> compose(Function<? super V, ? extends T> before)
現(xiàn)在讓我們說(shuō)我們想利用這個(gè)方法......例如讓我們定義一些基本類:
class Animal{}
class Dog extends Animal{}
class Fruit{}
class Apple extends Fruit{}
class Fish{}
class Tuna extends Fish{}
想象一下我們的函數(shù)和轉(zhuǎn)換定義如下:
Function<Animal, Apple> base = ...;
Function<Fish, Animal> transformation = ...;
我們可以組合這些函數(shù)compose來(lái)創(chuàng)建一個(gè)新函數(shù):
Function<Fish, Apple> composed = base.compose(transformation);
這一切都很好,但現(xiàn)在想象一下,在所需的輸出函數(shù)中,我們實(shí)際上只想用作Tuna輸入。如果我們不使用下界作為我們傳遞給的? super V輸入類型參數(shù),那么我們會(huì)得到一個(gè)編譯器錯(cuò)誤: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>
發(fā)生這種情況是因?yàn)檎{(diào)用的返回類型compose指定V為,Tuna而transformation另一方面指定其“ V”為Fish。所以現(xiàn)在當(dāng)我們嘗試傳遞transformation給compose編譯器時(shí)需要transformation接受 aTuna作為其V當(dāng)然Tuna不完全匹配Fish。
另一方面,代碼的原始版本 ( ? super V) 允許我們將其視為V下界(即它允許“逆變”與“不變” V)。編譯器不會(huì)遇到Tuna和之間的不匹配,而是Fish能夠成功應(yīng)用? super V計(jì)算結(jié)果為 的下限檢查Fish super Tuna,自 以來(lái)為真Tuna extends Fish。
對(duì)于另一種情況,假設(shè)我們的調(diào)用定義為:
Function<Animal, Apple> base = ...;
Function<Fish, Dog> transformation = ...;
Function<Fish, Apple> composed = base.compose(transformation);
如果我們沒(méi)有通配符,? extends T那么我們會(huì)得到另一個(gè)錯(cuò)誤:
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
對(duì)于你的第一個(gè)問(wèn)題;這些邊界實(shí)際上只在方法調(diào)用的上下文中起作用。在該方法的過(guò)程中,通配符將被解析為實(shí)際類型,就像? super V被解析為Fish和? extends T被解析為一樣Dog。如果沒(méi)有來(lái)自泛型簽名的信息,我們將無(wú)法讓編譯器知道可以在類型的方法上使用哪個(gè)類,因此不允許使用任何類。
添加回答
舉報(bào)