java - 为什么此 java 8 流操作评估为 Object 而不是 List<Object> 或只是 List?

标签 java java-8

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

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

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

编译失败。

$ 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() 在运行时做了正确的事情,但这种编译时行为是否符合预期?如果是这样,是否有“正确”的解决方法?

最佳答案

stream() , map() , collect() ,以及所有其他流方法都依赖于泛型类型才能正常工作。如果你使用一个原始类型作为输入,那么所有的泛型都会被删除,所有的东西都会变成一个原始类型。例如,如果您有一个 List<String> , 调用 stream()方法为您提供类型为 Stream<String> 的对象.

但是如果你从原始类型开始 List , 然后调用 stream()给你一个原始类型的对象 Stream相反。

map()方法有这样的声明:

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

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

    Stream map(Function mapper)

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

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

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

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

所以流管道的返回类型是 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));

关于java - 为什么此 java 8 流操作评估为 Object 而不是 List<Object> 或只是 List?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37214692/

相关文章:

java - 三元运算符在 JDK8 和 JDK10 上的行为差异

java - 从日期列表中获取最接近特定日期的日期?

java - 在Android中如何检查设备是否有前置闪光灯(Camera2 API)

java - 为什么 BufferedReader 的默认 char 缓冲区大小是 8192?

java - jmenubar 下方的 Eclipse 图标

java - Java 中的冒号是什么意思(第 2 部分)?

JavaScript 数据结构、JS 中的 ArrayList(Java)

java - Swing 计时器仅工作一次

java - 如何使用java 8流获取路径长度

java - 如何在 streamex 中对映射流的 double 值求和?