为什么Collector接口的组合器与重载的collect方法不一致?

在接口Stream有一个带有以下签名的重载方法collect()

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

还有另一个版本的collect(Collector collector) ,它接收具有前三个函数的对象。 与combiner Collector对应的接口Collector的属性具有签名BinaryOperator combiner()

在后一种情况下,Java API 8声明:

组合器函数可以将状态从一个参数折叠到另一个参数并返回该参数,或者可以返回新的结果容器。

为什么前者collect方法也没有收到BinaryOperator

“inline”(3-arg)版本的collect是专为你已经“躺着”的这些function而设计的。 例如:

 ArrayList list = stream.collect(ArrayList::new, ArrayList::add, ArrayList::addAll); 

要么

 BitSet bitset = stream.collect(BitSet::new, BitSet::set, BitSet::or); 

虽然这些仅仅是激励性的例子,但我们对类似的现有构建器类的探索是,现有组合器候选者的签名更适合转换为BiConsumer而不是BinaryOperator。 提供你所要求的“灵活性”会使这种过载在它被设计为支持的情况下变得不那么有用 – 也就是说,当你已经有了function,并且你不想做(或者学习制作)collections家只是为了收集它们。

另一方面,收集器具有更广泛的用途,因此具有额外的灵活性。

请记住, Stream.collect()的主要目的是支持Mutable Reduction 。 对于此操作,两个函数, 累加器组合器都旨在操纵可变容器,而不需要返回值。

因此,不坚持返回值更方便。 正如Brian Goetz指出的那样 ,这个决定允许重复使用许多现有的容器类型及其方法。 如果没有直接使用这些类型的能力,整个三参数collect方法将毫无意义。

相反, Collector接口是此操作的抽象 ,支持更多用例。 最值得注意的是,您甚至可以通过Collector为值类型(或具有值类型语义的类型)建模普通的,即非可变的Reducing操作。 在这种情况下, 必须有返回值,因为不能修改值对象本身。

当然,它并不意味着用作stream.collect(Collectors.reducing(…))而不是stream.reduce(…) 。 相反,这种抽象在组合收集器时很方便,例如groupingBy(…,reducing(…))

如果前一个collect方法收到BinaryOperator则以下示例将无法编译:

 ArrayList list = stream.collect(ArrayList::new, ArrayList::add, ArrayList::addAll); 

在这种情况下,编译器无法推断combiner的返回类型,并会产生编译错误。

因此,如果此版本的collect方法与Collector接口一致,那么它将促使更复杂地使用此版本的collect方法,这不是预期的。