簡體   English   中英

Java 8 Streams:為什么Collectors.toMap對於帶有通配符的泛型有不同的行為?

[英]Java 8 Streams: why does Collectors.toMap behave differently for generics with wildcards?

假設您有一個數字List List的值可以是IntegerDouble等類型。當您聲明這樣的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它全部collectMap (顯然下面的代碼只是一個例子來說明問題)。 讓我們開始流式傳輸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
  • 為什么lambda中的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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM