为什么这个 java 8 流操作评估为 Object 而不是 List<Object> 或只是 List?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/37214692/
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
Why does this java 8 stream operation evaluate to Object instead of List<Object> or just List?
提问by Bill
I'm working with a 3d party library, and they return Collections that lack type specifications (e.g. public List getFoo();
) , and I'm trying to convert their return types and return a list with a proper type.
我正在使用一个 3d 方库,它们返回缺少类型规范(例如public List getFoo();
)的集合,我正在尝试转换它们的返回类型并返回一个具有正确类型的列表。
I have created a simple example that demonstrates the problem. e.g.
我创建了一个简单的例子来演示这个问题。例如
editThe original question declared l2 as an ArrayList
rather than a List
, that is corrected now.
编辑最初的问题将 l2 声明为 anArrayList
而不是 a List
,现在已更正。
import java.util.List;
import java.util.ArrayList;
import java.util.stream.Collectors;
public class Foo {
public static void main(String[] args) {
ArrayList l = new ArrayList();
l.add(1);
l.add(2);
List<String> l2 = l.stream().map(Object::toString).collect(Collectors.toList());
}
}
This fails to compile.
这无法编译。
$ javac Foo.java
Foo.java:10: error: incompatible types: Object cannot be converted to List<String>
List<String> l2 = l.stream().map(Object::toString).collect(Collectors.toList());
^
1 error
If I modify the program slightly so it compiles and I can check the return type of the stream/collect operation. It "works", although I'd have to cast the result.
如果我稍微修改程序使其编译,我可以检查流/收集操作的返回类型。它“有效”,尽管我必须投射结果。
e.g.
例如
import java.util.ArrayList;
import java.util.stream.Collectors;
public class Foo {
public static void main(String[] args) {
ArrayList l = new ArrayList();
l.add(1);
l.add(2);
Object l2 = l.stream().map(Object::toString).collect(Collectors.toList());
System.out.println(l2.getClass());
}
}
Running this shows...
运行这个节目...
$ javac Foo.java
Note: Foo.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
$ java Foo
class java.util.ArrayList
As expected.
正如预期的那样。
So, Collectors.listCollector() does the right thing at runtime, but is this compile time behavior expected? If so, is there a "proper" way around it?
所以,Collectors.listCollector() 在运行时做正确的事情,但是这种编译时行为是预期的吗?如果是这样,是否有“适当”的方法来解决它?
回答by Stuart Marks
The stream()
, map()
, collect()
, and all the other stream methods rely on generic typing for things to work well. If you use a raw type as input, then all the generics are erased, and everything turns into a raw type. For example, if you have a List<String>
, calling the stream()
method gives you an object of type Stream<String>
.
在stream()
,map()
,collect()
,和所有其他流方法依赖于通用打字的东西很好地工作。如果您使用原始类型作为输入,那么所有泛型都将被删除,并且一切都变成原始类型。例如,如果您有一个List<String>
,则调用该stream()
方法会为您提供一个类型为 的对象Stream<String>
。
But if you start out with a raw type List
, then calling stream()
gives you an object of the raw type Stream
instead.
但是如果你从一个原始类型开始List
,那么调用stream()
会给你一个原始类型的对象Stream
。
The map()
method has this declaration:
该map()
方法具有以下声明:
<R> Stream<R> map(Function<? super T,? extends R> mapper)
but since it's called on a raw Stream
, it's as if the declaration is
但由于它是在 raw 上调用的Stream
,就好像声明是
Stream map(Function mapper)
so the result of calling map()
is simply a Stream
. The signature of collect()
is filled with generics:
所以调用的结果map()
只是一个Stream
. 的签名collect()
充满了泛型:
<R> R collect(Supplier<R> supplier,
BiConsumer<R,? super T> accumulator,
BiConsumer<R,R> combiner)
When called with a raw type, it gets erased to
当使用原始类型调用时,它会被擦除为
Object collect(Supplier supplier,
BiConsumer accumulator,
BiConsumer combiner)
so the return type of your stream pipeline is Object
when its source is a raw type. Obviously this isn't compatible with ArrayList<String>
and you get a compilation error.
所以你的流管道的返回类型是Object
它的源是原始类型。显然这不兼容,ArrayList<String>
并且您会收到编译错误。
To fix this, bring your types into the generic world as soon as possible. This will probably involve unchecked casts, and you should probably suppress the warning. Of course, do this only if you're sure the returned collection contains objects only of the expected type.
要解决此问题,请尽快将您的类型带入泛型世界。这可能涉及未经检查的强制转换,您可能应该取消警告。当然,仅当您确定返回的集合仅包含预期类型的对象时才执行此操作。
In addition, as Hank Dpointed out, the toList()
collector returns a List
, not an ArrayList
. You can either assign the return value to a variable of type List
, or you can use toCollection()
to create an instance of ArrayList
explicitly.
此外,正如Hank D指出的,toList()
收集器返回一个List
,而不是一个ArrayList
。您可以将返回值分配给类型为 的变量List
,也可以用于显式toCollection()
创建 的实例ArrayList
。
Here's the code with these modifications included:
这是包含这些修改的代码:
@SuppressWarnings("unchecked")
ArrayList<Integer> l = (ArrayList<Integer>)getRawArrayList();
l.add(1);
l.add(2);
ArrayList<String> l2 = l.stream()
.map(Object::toString)
.collect(Collectors.toCollection(ArrayList::new));
回答by Hank D
Collectors.toList() returns a List, not an ArrayList. This will compile:
Collectors.toList() 返回一个列表,而不是一个 ArrayList。这将编译:
public static void main(String[] args) {
ArrayList<?> l = getRawArrayList();
List<String> l2 = l.stream().map(Object::toString).collect(Collectors.toList());
}