Java 8 ストリーム: ワイルドカードを使用したジェネリックの場合、Collectors.toMap の動作が異なるのはなぜですか? 質問する

Java 8 ストリーム: ワイルドカードを使用したジェネリックの場合、Collectors.toMap の動作が異なるのはなぜですか? 質問する

数値のがあるとしますList。 の値は、などListの型になります。 このような を宣言する場合、ワイルドカード ( ) を使用して宣言することも、ワイルドカードなしで宣言することもできます。IntegerDoubleList?

final List<Number> numberList = Arrays.asList(1, 2, 3D);
final List<? extends Number> wildcardList = Arrays.asList(1, 2, 3D);

そこで、streamを に渡してList、を使用してcollectすべてを に渡したいと思います(明らかに、以下のコードは問題を説明するための例にすぎません)。 をストリーミングすることから始めましょう:MapCollectors.toMapnumberList

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()
場所: java.lang.Object 型の変数番号

numberコンパイラ エラーから、ラムダ内の がObjectではなくとして扱われていることは明らかですNumber

さて、私の質問は次の通りです。

  • のワイルドカード バージョンを収集するときにList、 の非ワイルドカード バージョンのように動作しないのはなぜですかList?
  • numberラムダ内の変数がObjectではなく とみなされるのはなぜですかNumber?

ベストアンサー1

型推論が正しく行われないのです。型引数を明示的に指定すると、期待どおりに動作します。

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 のバグです:推論ではキャプチャ変数を上限にマッピングしてはならないマウリツィオ・チマダモーレによれば、

修正を試みたが、8 でケースが壊れていたため取り消されたため、8 ではより保守的な修正を行い、9 では完全な修正を行うことにしました。

どうやら修正はまだプッシュされていないようです。(ジョエル・ボルグレン・フランク正しい方向を指し示してくれてありがとう。

おすすめ記事