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

Java 8 Streams:为什么Collectors.toMap对于带有通配符的泛型有不同的行为?,java,generics,lambda,java-8,collectors,Java,Generics,Lambda,Java 8,Collectors,假设您有一个编号列表。列表中的值可以是整数、双精度等类型。当您声明这样的列表时,可以使用通配符(?)或不使用通配符进行声明 final List<Number> numberList = Arrays.asList(1, 2, 3D); final List<? extends Number> wildcardList = Arrays.asList(1, 2, 3D); 但是,我不能在通配符列表上执行相同的操作: final List<Number> nu

假设您有一个编号列表。
列表
中的值可以是
整数
双精度
等类型。当您声明这样的
列表
时,可以使用通配符(
)或不使用通配符进行声明

final List<Number> numberList = Arrays.asList(1, 2, 3D);
final List<? extends Number> wildcardList = Arrays.asList(1, 2, 3D);
但是,我不能在
通配符列表上执行相同的操作:

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));
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));

final List类型推断不正确。如果显式提供类型参数,它将按预期工作:

List<? extends Number> wildCardList = Arrays.asList(1, 2, 3D);
wildCardList.stream().collect(Collectors.<Number, Integer, Number>toMap(
                                  number -> Integer.valueOf(number.intValue()),
                                  number -> number));
List这是因为,在第一种情况下,您声明了
List
,所以当您编写
number->Integer.valueOf(number.intValue())
时,编译器不会反对,因为变量
number
的类型是
java.lang.number


但是在第二种情况下,您声明了
final List表单的声明
List,您可以执行以下操作:

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

numberList.stream().collect(Collectors.toMap(Number::intValue, Function.identity()));
final List numberList=Arrays.asList(1,2,3D,4D);
numberList.stream().collect(Collectors.toMap(Number::intValue,Function.identity());

问题中指出了编译器将type对象分配给number变量的事实。我想问题是为什么它不是Number类型。具有讽刺意味的是,您解释中的代码甚至没有编译为
Collector
对于这个收集器来说不是有效的类型。由于值提取器函数
number->number
,编译器知道
采集器必须是
采集器,而不是真正的改进。缺少的是解释为什么编译器应该推断
收集器
…@Holger答案中有一个指向JLS的链接。这还不够吗?不,这还不够。解释和链接显然是两件完全不同的事情。我接受打赌。我知道,而且我打赌,如果规范中完全涵盖了故障,那么很难找出它在这里不起作用的原因。我只想说,对于
函数来说,确实,它使用
jdk1.9.0
编译……这是一个有趣的发现。就在我以为自己已经完全掌握了形势的时候-/结果是一个javac错误。更新了我的答案。
?扩展数字
N扩展数字
两种不同的东西?后者用于限制N的可能类型,而
?扩展编号
是类型。我认为您的示例符合条件,是一个很好的解决方法,因此+1。@zeroflagL:当您想要修改集合,或者更一般地说,从泛型类的实例中获取值并将其传递回同一实例时,需要这种helper方法。或者当您需要显式引用类型时。但通常情况下,对于一个简单的只读操作,您不需要这样的变通方法,比如在该上下文中(如此处),
?extends X
实际上相当于
ArbiraryTypeParameter extends X
。两者都意味着一个未知类型,必须分配给
X
“我不知道通配符和其他类型在这方面是否有区别”:您可能已经看到JLS明确区分了“类型”和“通配符”(特别是在18.2.3中),这意味着“通配符”不是“类型”!!只是说说而已。
    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);
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));
}
final List<? extends Number> wildCardList = Arrays.asList(1, 2, 3D);
doTheThingWithoutWildCards(wildCardList); // or:
doTheThingWithoutWildCards(Arrays.asList(1, 2, 3D));
final List<Number> numberList = Arrays.asList(1, 2, 3D, 4D);

numberList.stream().collect(Collectors.toMap(Number::intValue, Function.identity()));