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



接口Stream<T>中有一个重载方法collect(),其签名如下:

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

还有另一个版本的collect(Collector<? super T,A,R> collector),它接收一个具有前三个函数的对象。combiner对应的接口Collector的属性签名为BinaryOperator<A> combiner()

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

组合函数可以将一个实参的状态折叠到另一个实参中并返回,也可以返回一个新的结果容器。

为什么以前的collect方法也没有收到BinaryOperator<R> ?

collect的"内联" (3-arg)版本是专为当你已经有这些函数"躺着"。例如:

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

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

虽然这些只是激励的例子,但我们对类似的现有构建器类的探索发现,现有组合器候选类的签名更适合转换为bicconsumer,而不是BinaryOperator。提供你所要求的"灵活性"会使这种重载在它设计支持的情况下变得不那么有用——当你已经有了函数,并且你不想仅仅为了收集它们而创建(或学习创建)一个Collector时。

另一方面,

Collector具有更广泛的用途,因此值得额外的灵活性。

请记住,Stream.collect()的主要目的是支持可变缩减。对于该操作,累加器组合器这两个函数都用于操作可变容器,不需要返回值。

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

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

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

如果之前的collect方法接收到一个BinaryOperator<R>,那么下面的例子将无法编译:

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

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

因此,如果这个版本的collect方法与Collector接口一致,那么它将促进这个版本的collect方法的更复杂的使用,这不是我们想要的。

最新更新