Java 8 Streams:为什么Collectors.toMap对于带有通配符的泛型有不同的行为?

was*_*ren 27 java generics lambda java-8 collectors

假设你有一个List数字.在该值List可以是类型的Integer,Double等等.当你声明这样的List有可能使用一个通配符(声明它?)或不用一个通配符.

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

所以,现在我想streamListcollect它所有的Map使用Collectors.toMap(显然下面的代码只是为了说明问题的例子).让我们开始流式传输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));
Run Code Online (Sandbox Code Playgroud)

但是,我不能对以下内容做同样的操作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));
Run Code Online (Sandbox Code Playgroud)

编译器在调用时抱怨number.intValue()以下消息:

Test.java:找不到符号
符号:方法intValue()
位置:java.lang.Object类型的变量数

从编译器错误中可以看出number,lambda中的内容被视为a Object而不是a Number.

所以,现在问我的问题:

  • 在收集通配符版本时List,为什么它不像非通配符版本那样工作List
  • 为什么numberlambda中的变量被认为是Object而不是Number

aio*_*obe 36

这种类型推断并没有使它正确.如果明确提供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));
Run Code Online (Sandbox Code Playgroud)

这是一个已知的javac错误:推断不应该将捕获变量映射到它们的上限.根据Maurizio Cimadamore的说法,

尝试修复然后因为它在8中破坏案例而退出,所以我们在8中做了一个更保守的修复而在9中完成了

显然还没有推出修复方案.(感谢 JoelBorggrén-Franck指出我正确的方向.)