为什么这个java 8流操作的计算结果为对象而不是List<Object>或只是List?

2022-09-01 15:49:26

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

我创建了一个简单的示例来演示问题。例如:

编辑原来的问题宣布 l2 为 a 而不是 a,现在已得到纠正。ArrayListList

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

这无法编译。

$ 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

如果我稍微修改程序,以便它编译,我可以检查流/收集操作的返回类型。它“有效”,尽管我必须投射结果。

例如:

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

运行此显示...

$ javac Foo.java
Note: Foo.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
$ java Foo
class java.util.ArrayList

不出所料。

因此,Collectors.listCollector() 在运行时执行正确的操作,但是这种编译时行为是预期的吗?如果是这样,是否有“适当”的方法来解决这个问题?


答案 1

、 、 和所有其他流方法都依赖于泛型类型才能正常工作。如果使用原始类型作为输入,则将擦除所有泛型,并且所有内容都将转换为原始类型。例如,如果您有一个 ,则调用该方法将为您提供一个类型为 的对象。stream()map()collect()List<String>stream()Stream<String>

但是,如果您从 raw 类型开始,则调用会为您提供原始类型的对象。Liststream()Stream

该方法具有以下声明:map()

    <R> Stream<R> map(Function<? super T,? extends R> mapper)

但是由于它是在原始上调用的,就好像声明是Stream

    Stream map(Function mapper)

因此调用的结果只是一个 .的签名填充了泛型:map()Streamcollect()

    <R> R collect(Supplier<R> supplier,
                  BiConsumer<R,? super T> accumulator,
                  BiConsumer<R,R> combiner)

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

    Object collect(Supplier supplier,
                   BiConsumer accumulator,
                   BiConsumer combiner)

因此,流管道的返回类型是其源为原始类型时。显然,这与不兼容,并且您会收到编译错误。ObjectArrayList<String>

若要解决此问题,请尽快将类型引入泛型世界。这可能涉及未经检查的强制转换,并且您可能应该禁止显示警告。当然,仅当您确定返回的集合仅包含预期类型的对象时,才执行此操作。

此外,正如 Hank D 所指出的,收集器返回 a ,而不是 .您可以将返回值赋给 类型的变量,也可以使用 来显式创建 的实例。toList()ListArrayListListtoCollection()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));

答案 2

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

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

}

推荐