为什么在collect()之后调用stream()时得到Stream <object>?

时间:2018-09-27 17:30:46

标签: java java-8 java-stream

考虑以下示例,该示例尚未编译:

List<Integer> list = Arrays.asList(1, 2, -3, 8);

        list.stream()
                .filter(x -> x > 0)
                .collect(ArrayList::new, ArrayList::add, ArrayList::addAll)
                .stream() // Stream<Object>
                .map(x -> x * 2)
                .forEach(System.out::println);

如果我替换

.collect(ArrayList::new, ArrayList::add, ArrayList::addAll)

使用

.collect(Collectors.toList())

代码将被编译。 所以问题是我该如何用供应商和累加器写collect()(我需要它)才能在其后调用stream()

1 个答案:

答案 0 :(得分:8)

似乎您已经使用ArrayListArrayList::new构造函数进行了原始方法引用。

没有通过以下方式推断出类型参数:

.collect(ArrayList::new, ArrayList::add, ArrayList::addAll)

3-argument overload of collect期望3个参数,第一个是Supplier<R>。此时,collect方法的类型参数RT(此处为Integer)之间没有任何联系。唯一的推断方法是通过第二个参数BiConsumer<R, ? super T>。这里有ArrayList::add,这也使编译器也无法推断R

您必须提供第一个参数(供应商)中的R。您可以向该类提供显式类型参数以在方法引用上创建。

.collect(ArrayList<Integer>::new, ArrayList::add, ArrayList::addAll)

这将编译,并且输出符合预期:

2
4
16

使用Collectors.toList()时,仅提供一个参数。

  

public static <T> Collector<T,?,List<T>> toList()

这里,只有一个类型参数T,因此编译器可以正确地推断出此TInteger,因此创建了一个List<Integer>,从而允许代码编译。 Collector返回的类型参数将T绑定到List<T>,从而允许编译器执行类型推断。

请注意,这首先是必要的,因为没有目标类型可以帮助类型推断。您可以继续进行流操作,最后只需调用System.out.println,这可能需要Object

如果您有此代码:

List<Integer> modified = list.stream()
            .filter(x -> x > 0)
            .collect(ArrayList::new, ArrayList::add, ArrayList::addAll);

然后,目标类型推断将为Integer的类型参数提供ArrayList::new。这也可以编译。