Java 8 Streams:为什么 Collectors.toMap 对于带有通配符的泛型有不同的表现?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27890241/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java 8 Streams: why does Collectors.toMap behave differently for generics with wildcards?
提问by wassgren
Assume that you have a List
of numbers. The values in the List
can be of type Integer
, Double
etc. When you declare such a List
it is possible to declare it using a wildcard (?
) or without a wildcard.
假设你有一个List
数字。在该值List
可以是类型的Integer
,Double
等等。当你声明这样的List
有可能使用一个通配符(声明它?
)或不用一个通配符。
final List<Number> numberList = Arrays.asList(1, 2, 3D);
final List<? extends Number> wildcardList = Arrays.asList(1, 2, 3D);
So, now I want to stream
over the List
and collect
it all to a Map
using the Collectors.toMap
(obviously the code below is just an example to illustrate the problem). Lets start off by streaming the numberList
:
所以,现在我想stream
在List
和collect
它所有的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));
But, I can not do the same operation on the wildcardList
:
但是,我不能对以下内容进行相同的操作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));
The compiler complains on the call to number.intValue()
with the following message:
编译器在调用 时抱怨number.intValue()
以下消息:
Test.java: cannot find symbol
symbol: method intValue()
location: variable number of type java.lang.Object
Test.java:找不到符号
符号:方法 intValue()
位置:java.lang.Object 类型的变量数
From the compiler error it is obvious that the number
in the lambda is treated as an Object
instead of as a Number
.
从编译器错误很明显,number
lambda 中的 被视为 anObject
而不是 a Number
。
So, now to my question(s):
所以,现在我的问题:
- When collecting the wildcard version of the
List
, why is it not working like the non-wildcard version of theList
? - Why is the
number
variable in the lambda considered to be anObject
instead of aNumber
?
- 在收集 的通配符版本时
List
,为什么它不像 的非通配符版本那样工作List
? - 为什么
number
lambda 中的变量被认为是 anObject
而不是 aNumber
?
采纳答案by aioobe
It's the type inference that doesn't get it right. If you provide the type argument explicitly it works as expected:
是类型推断不正确。如果您明确提供类型参数,它会按预期工作:
List<? extends Number> wildCardList = Arrays.asList(1, 2, 3D);
wildCardList.stream().collect(Collectors.<Number, Integer, Number>toMap(
number -> Integer.valueOf(number.intValue()),
number -> number));
This is a known javac bug: Inference should not map capture variables to their upper bounds. The status, according to Maurizio Cimadamore,
这是一个已知的 javac 错误:推理不应将捕获变量映射到它们的上限。根据 Maurizio Cimadamore 的说法,这种状态,
a fix was attempted then backed out as it was breaking cases in 8, so we went for a more conservative fix in 8 while doing the full thing in 9
尝试修复然后因为它在 8 中破坏案例而退出,因此我们在 8 中进行了更保守的修复,同时在 9 中完成了全部工作
Apparently the fix has not yet been pushed. (Thanks to Joel Borggrén-Franckfor pointing me in the right direction.)
显然,该修复程序尚未推送。(感谢Joel Borggrén-Franck为我指明了正确的方向。)
回答by sol4me
This is due to type inference, In first case you declared List<Number>
so compiler have nothing against when you write number -> Integer.valueOf(number.intValue())
because type of variable number
isjava.lang.Number
这是由于类型推断,在第一种情况下,您声明List<Number>
编译器在编写时没有任何反对,number -> Integer.valueOf(number.intValue())
因为变量的类型number
是java.lang.Number
But in second case you declared final List<? extends Number> wildCardList
due to which
Collectors.toMap
is translated to something like Collectors.<Object, ?, Map<Object, Number>toMap
E.g.
但在第二种情况下,你声明final List<? extends Number> wildCardList
由于 which
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);
As a result of which in expression
因此,在表达式中
number -> Integer.valueOf(number.intValue()
number -> Integer.valueOf(number.intValue()
type of variable number
is Objectand there is no method intValue()
defined in class Object. Hence you get compilation error.
变量类型number
是ObjectintValue()
,类Object中没有定义方法。因此你会得到编译错误。
What you need is to pass collector type arguments which helps the compiler to resolve intValue()
error E.g.
您需要的是传递收集器类型参数,这有助于编译器解决 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);
Moreover you can use method reference Number::intValue
instead of number -> Integer.valueOf(number.intValue())
此外,您可以使用方法引用Number::intValue
而不是number -> Integer.valueOf(number.intValue())
For more details on Type Inference in Java 8 please refer here.
有关 Java 8 中类型推断的更多详细信息,请参阅此处。
回答by Holger
The declaration of the form List<? extends Number> wildcardList
implies a “list with an unknown type which is Number
or a subclass of Number
”. Interestingly, the same kind of list with unknown type works, if the unknown type is referred by a name:
形式的声明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));
}
Here, N
is still “an unknown type being Number
or a subclass of Number
” but you can process the List<N>
as intended. You can assign the List<? extends Number>
to a List<N>
without problems as the constraint that the unknown type extends Number
is compatible.
在这里,N
仍然是“未知类型Number
或子类Number
”,但您可以List<N>
按预期处理。您可以毫无问题地将 分配List<? extends Number>
给 aList<N>
作为未知类型extends Number
兼容的约束。
final List<? extends Number> wildCardList = Arrays.asList(1, 2, 3D);
doTheThingWithoutWildCards(wildCardList); // or:
doTheThingWithoutWildCards(Arrays.asList(1, 2, 3D));
The chapter about Type Inferenceis not an easy read. I don't know if there is a difference between wildcards and other types in this regard, but I don't think that there should be. So its eithera compiler bugor a limitation by specification but logically,there is no reason why the wildcard shouldn't work.
关于类型推断的章节并不容易阅读。我不知道通配符和其他类型在这方面是否有区别,但我认为不应该有。所以它要么是编译器错误,要么是规范限制,但从逻辑上讲,通配符没有理由不工作。
回答by Rory G
You can do:
你可以做:
final List<Number> numberList = Arrays.asList(1, 2, 3D, 4D);
numberList.stream().collect(Collectors.toMap(Number::intValue, Function.identity()));