[英]Java 8 Streams: why does Collectors.toMap behave differently for generics with wildcards?
假設您有一個數字List
。 List
的值可以是Integer
, Double
等類型。當您聲明這樣的List
,可以使用通配符( ?
)或不使用通配符來聲明它。
final List<Number> numberList = Arrays.asList(1, 2, 3D);
final List<? extends Number> wildcardList = Arrays.asList(1, 2, 3D);
所以,現在我想stream
傳輸List
並使用Collectors.toMap
collect
它全部collect
到Map
(顯然下面的代碼只是一個例子來說明問題)。 讓我們開始流式傳輸numberList
:
final List<Number> numberList = Arrays.asList(1, 2, 3D, 4D);
numberList.stream().collect(Collectors.toMap(
// Here I can invoke "number.intValue()" - the object ("number") is treated as a Number
number -> Integer.valueOf(number.intValue()),
number -> number));
但是,我不能對wildcardList
執行相同的操作:
final List<? extends Number> wildCardList = Arrays.asList(1, 2, 3D);
wildCardList.stream().collect(Collectors.toMap(
// Why is "number" treated as an Object and not a Number?
number -> Integer.valueOf(number.intValue()),
number -> number));
編譯器在調用number.intValue()
時抱怨以下消息:
Test.java:找不到符號
符號:方法intValue()
location:java.lang.Object類型的變量數
從編譯器錯誤中可以看出,lambda中的number
被視為Object
而不是Number
。
所以,現在問我的問題:
List
,它為什么不工作,如非通配符版本的List
? number
變量被認為是Object
而不是Number
? 這種類型推斷並沒有使它正確。 如果明確提供type參數,它將按預期工作:
List<? extends Number> wildCardList = Arrays.asList(1, 2, 3D);
wildCardList.stream().collect(Collectors.<Number, Integer, Number>toMap(
number -> Integer.valueOf(number.intValue()),
number -> number));
這是一個已知的javac錯誤: 推斷不應該將捕獲變量映射到它們的上限 。 根據Maurizio Cimadamore的說法,
嘗試修復然后因為它在8中破壞案例而退出,所以我們在8中做了一個更保守的修復而在9中完成了
顯然修復尚未推出。 (感謝JoelBorggrén-Franck指出我正確的方向。)
表格List<? extends Number> wildcardList
的聲明 List<? extends Number> wildcardList
意味着“列表具有未知類型是Number
或子類Number
”。 有趣的是,如果未知類型由名稱引用,則具有未知類型的相同類型的列表有效:
static <N extends Number> void doTheThingWithoutWildCards(List<N> numberList) {
numberList.stream().collect(Collectors.toMap(
// Here I can invoke "number.intValue()" - the object is treated as a Number
number -> number.intValue(),
number -> number));
}
這里, N
仍然是“未知類型存在Number
或子類Number
”,但是可以處理所述List<N>
如預期。 您可以指定List<? extends Number>
List<? extends Number>
為List<N>
沒有問題,因為未知類型extends Number
的約束extends Number
是兼容的。
final List<? extends Number> wildCardList = Arrays.asList(1, 2, 3D);
doTheThingWithoutWildCards(wildCardList); // or:
doTheThingWithoutWildCards(Arrays.asList(1, 2, 3D));
關於類型推斷的章節並不容易閱讀。 我不知道在這方面通配符和其他類型之間是否存在差異,但我認為不應該存在。 因此,
無論
是編譯器錯誤
還是規范限制,但從邏輯上講,
通配符都沒有理由不起作用。
這是由於類型推斷 ,在第一種情況下,您聲明了List<Number>
因此當您編寫number -> Integer.valueOf(number.intValue())
時,編譯器沒有任何反對number -> Integer.valueOf(number.intValue())
因為變量number
類型是java.lang.Number
但在第二種情況下你宣布了final List<? extends Number> wildCardList
final List<? extends Number> wildCardList
因為Collectors.toMap
被轉換為類似Collectors.<Object, ?, Map<Object, Number>toMap
Eg
final List<? extends Number> wildCardList = Arrays.asList(1, 2, 3D);
Collector<Object, ?, Map<Object, Object>> collector = Collectors.toMap(
// Why is number treated as an Object and not a Number?
number -> Integer.valueOf(number.intValue()),
number -> number);
wildCardList.stream().collect(collector);
由此表達
number -> Integer.valueOf(number.intValue()
變量的類型number
是對象 ,並且沒有方法intValue()
類Object中所定義。 因此,您會收到編譯錯誤。
你需要的是傳遞收集器類型參數,這有助於編譯器解決intValue()
錯誤例如
final List<? extends Number> wildCardList = Arrays.asList(1, 2, 3D);
Collector<Number, ?, Map<Integer, Number>> collector = Collectors.<Number, Integer, Number>toMap(
// Why is number treated as an Object and not a Number?
Number::intValue,
number -> number);
wildCardList.stream().collect(collector);
此外,您可以使用方法引用Number::intValue
而不是number -> Integer.valueOf(number.intValue())
有關Java 8中類型推斷的更多詳細信息,請參閱此處 。
你可以做:
final List<Number> numberList = Arrays.asList(1, 2, 3D, 4D);
numberList.stream().collect(Collectors.toMap(Number::intValue, Function.identity()));
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.