请考虑以下示例,该示例未编译:
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()
?
您似乎已经使用ArrayList::new
对ArrayList
构造函数进行了原始方法引用。
类型参数不是用以下方法推断的:
.collect(ArrayList::new, ArrayList::add, ArrayList::addAll)
collect
的 3 参数重载需要 3 个参数,第一个参数是Supplier<R>
。 此时,collect
方法的类型参数R
与此处Integer
T
之间没有联系。 推断这一点的唯一方法是通过第二个参数,一个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
,所以编译器可以正确地推断这个T
是Integer
的,所以创建了一个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
。 这也编译。