为什么这个java 8流操作求值为Object而不是List <Object>或只是List?

Bil*_*ill 21 java java-8

我正在使用3d派对库,他们返回缺少类型规范的集合(例如public List getFoo();),并且我正在尝试转换它们的返回类型并返回具有适当类型的列表.

我创建了一个演示问题的简单示例.例如

编辑原始问题将l2声明为a ArrayList而不是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());
    }
}
Run Code Online (Sandbox Code Playgroud)

这无法编译.

$ 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
Run Code Online (Sandbox Code Playgroud)

如果我稍微修改程序,那么它编译,我可以检查流/收集操作的返回类型.虽然我不得不施展结果,但它"有效".

例如

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());
    }
}
Run Code Online (Sandbox Code Playgroud)

运行此节目......

$ javac Foo.java
Note: Foo.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
$ java Foo
class java.util.ArrayList
Run Code Online (Sandbox Code Playgroud)

正如所料.

因此,Collectors.listCollector()在运行时做了正确的事情,但是这是预期的编译时行为吗?如果是这样,它周围是否有"正确"的方式?

Stu*_*rks 22

stream(),map(),collect(),和所有其他流方法依赖于通用打字的东西很好地工作.如果使用原始类型作为输入,则所有泛型都将被删除,并且所有内容都将变为原始类型.例如,如果你有一个List<String>,调用该stream()方法会给你一个类型的对象Stream<String>.

但是如果你从一个原始类型开始List,那么调用stream()会给你一个原始类型的对象Stream.

map()方法有这个声明:

    <R> Stream<R> map(Function<? super T,? extends R> mapper)
Run Code Online (Sandbox Code Playgroud)

但是因为它被称为原始的Stream,就好像声明一样

    Stream map(Function mapper)
Run Code Online (Sandbox Code Playgroud)

所以调用的结果map()只是一个Stream.签名collect()充满了泛型:

    <R> R collect(Supplier<R> supplier,
                  BiConsumer<R,? super T> accumulator,
                  BiConsumer<R,R> combiner)
Run Code Online (Sandbox Code Playgroud)

当使用原始类型调用时,它将被删除

    Object collect(Supplier supplier,
                   BiConsumer accumulator,
                   BiConsumer combiner)
Run Code Online (Sandbox Code Playgroud)

所以流管道的返回类型是Object它的源是原始类型.显然这是不兼容的ArrayList<String>,你得到一个编译错误.

要解决此问题,请尽快将您的类型纳入通用世界.这可能涉及未经检查的强制转换,您应该抑制警告.当然,只有在您确定返回的集合包含仅具有预期类型的​​对象时才执行此操作.

另外,正如Hank D指出的那样,toList()收藏家返回一个List而不是一个ArrayList.您可以将返回值分配给类型的变量List,也可以使用显式toCollection()创建实例ArrayList.

以下是包含以下修改的代码:

    @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));
Run Code Online (Sandbox Code Playgroud)


Han*_*k D 5

Collectors.toList() 返回一个 List,而不是 ArrayList。这将编译:

public static void main(String[] args) {
    ArrayList<?> l = getRawArrayList();
    List<String> l2 = l.stream().map(Object::toString).collect(Collectors.toList());

}
Run Code Online (Sandbox Code Playgroud)