![](/img/trans.png)
[英]use java.util.function.Function to implement Factory Design Pattern
[英]Question about Java generics and design of java.util.function.Function
示例: 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
我已閱讀以下問題的答案“ capture#1-of ? extends Object is not 適用”
您正在使用通用通配符。 您不能執行添加操作,因為類類型不確定。 您不能添加/放置任何內容(空值除外)-- Aniket Thakur
官方文檔:通配符永遠不會用作泛型方法調用、泛型類實例創建或超類型的類型參數
但是為什么list.add(student)
編譯成功呢?
java.util.function.Function
設計public 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<? super V, ? extends T>
Function<? super V, ? extends T>
而不是Function<V,T>
當返回的類型是Function<V,R>
和輸入的類型是V
? (它還可以通過編譯靈活使用)
要理解這些問題,您必須了解泛型如何與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
方法),具有供應類型的對象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
? super T
是:當我們使用andThen
方法組合兩個Consumer
,假設前一個Consumer
接受一個類型為T
的對象,我們期望后者接受一個類型no more than T
的對象,因此它不會嘗試訪問T
沒有的任何成員。
因此,而不是簡單地將Consumer<T> after
寫Consumer<T> after
但Consumer<? super T> after
Consumer<? super T> after
,我們允許前一個消費者(類型T
)與一個消費者結合,該消費者接受的對象不完全是T
類型,但可能比T
小,這是為了方便covariance
。 這使得以下代碼聽起來:
Consumer<Student> stu = (student) -> {};
Consumer<Person> per = (person) -> {};
stu.andThen(per);
compose
同樣的考慮, Function
類型的compose
方法也適用。
IMO 這可能是 vanilla Java 中最復雜的概念。 所以讓我們把它分解一下。 我將從你的第二個問題開始。
Function<T, R>
接受類型為T
的實例t
並返回類型為R
的實例r
。 有了繼承這意味着,你可以提供一個實例foo
類型的Foo
,如果Foo extends T
,同樣返回bar
類型的Bar
,如果Bar 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
? super V
作為我們傳遞給compose
的Function
的輸入類型參數,那么我們會得到一個編譯器錯誤:
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
來接受一個Tuna
作為它的V
,當然Tuna
並不完全匹配Fish
。
另一方面,代碼的原始版本( ? super V
)允許我們將V
視為下限(即它允許V
“逆變”與“不變”)。 編譯器能夠成功應用下限檢查,而不是遇到Tuna
和Fish
之間的不匹配? super V
計算為Fish super Tuna
? super V
,這是真的,因為Tuna extends Fish
。
對於另一種情況,假設我們的調用定義為:
Function<Animal, Apple> base = ...;
Function<Fish, Dog> transformation = ...;
Function<Fish, Apple> composed = base.compose(transformation);
如果我們沒有通配符? extends T
? 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>
通配符? extends T
? extends T
允許這樣做,因為T
解析為Animal
並且通配符解析為Dog
,這可以滿足約束Dog extends Animal
。
對於你的第一個問題; 這些邊界實際上只在方法調用的上下文中起作用。 在該方法的過程中,通配符將被解析為實際類型,就像? super V
? super V
被解析到Fish
和? extends T
? extends T
被解析為Dog
。 如果沒有來自泛型簽名的信息,我們將無法讓編譯器知道可以在類型的方法上使用什么類,因此不允許使用任何類。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.