亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

關于 java.util.function.Function 的 Java 泛型和設計的問題

關于 java.util.function.Function 的 Java 泛型和設計的問題

達令說 2023-06-14 14:43:06
關于通配符的問題例子:Student extends Person? ? Person person = new Person();? ? Student student = new Student();? ? List<? super Student> list = new ArrayList<>();? ? list.add(student); // success? ? list.add(person); // compile error? ? List<? extends Person> list2 = new ArrayList<>();? ? list2.add(person); // compile error? ? list2.add(student);// compile error您正在使用通用通配符。您無法執行添加操作,因為類類型不確定。你不能添加/放置任何東西(null 除外)&mdash;&mdash;Aniket Thakur官方文檔:通配符從不用作泛型方法調用、泛型類實例創建或超類型的類型參數但是為什么能list.add(student)編譯成功呢?的設計java.util.function.Functionpublic interface Function<T, R>{? ? //...? ? default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {? ? ? ? Objects.requireNonNull(before);? ? ? ? return (V v) -> apply(before.apply(v));? ? }}為什么before設計為Function<? super V, ? extends T>而不是Function<V,T>當返回類型是Function<V,R>并且輸入類型是V?(還可以通過編譯靈活使用)
查看完整描述

2 回答

?
HUX布斯

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


查看完整回答
反對 回復 2023-06-14
?
滄海一幻覺

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。如果沒有來自泛型簽名的信息,我們將無法讓編譯器知道可以在類型的方法上使用哪個類,因此不允許使用任何類。


查看完整回答
反對 回復 2023-06-14
  • 2 回答
  • 0 關注
  • 149 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號