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

sta*_*one 4 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);
Run Code Online (Sandbox Code Playgroud)

如果我更换

.collect(ArrayList::new, ArrayList::add, ArrayList::addAll)
Run Code Online (Sandbox Code Playgroud)

同

.collect(Collectors.toList())
Run Code Online (Sandbox Code Playgroud)

代码将被编译.那么问题是我如何collect()用供应商和累加器(我需要它)来编写能够在之后调用stream()它?

rge*_*man 8

您似乎已经使用ArrayList构造函数创建了一个原始方法引用ArrayList::new.

类型参数未推断为:

.collect(ArrayList::new, ArrayList::add, ArrayList::addAll)
Run Code Online (Sandbox Code Playgroud)

期望3个参数的3参数重载collect,第一个是a Supplier<R>.在这一点上是没有关系的collect方法的类型参数R和T它是Integer在这里.推断这个的唯一方法是通过第二个参数,a BiConsumer<R, ? super T>.这里有ArrayList::add,这使编译器无法推断R.

您必须提供R第一个参数中的内容,即供应商.您可以向类提供显式类型参数以在方法引用上创建.

.collect(ArrayList<Integer>::new, ArrayList::add, ArrayList::addAll)
Run Code Online (Sandbox Code Playgroud)

这个编译,输出是预期的:

2
4
16
Run Code Online (Sandbox Code Playgroud)

使用时Collectors.toList(),您只提供一个参数.

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

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

请注意,这只是首先需要,因为没有目标类型来帮助进行类型推断; 你继续使用流操作,并System.out.println在最后调用,这可能需要一个Object.

如果你有这个代码:

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

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