Refactor of Collector interface

Remi Forax forax at univ-mlv.fr
Sat Feb 9 07:57:11 PST 2013


On 02/08/2013 05:36 PM, Brian Goetz wrote:
> Your subjective sense is accurate, which is why I brought this up. 
> This may be an example where is better to depart from the traditional 
> approach.
>
> To your question, it depends what you mean by "purely to do with an 
> implementor."  Collector *users* are going to be burdened with the 
> performance consequences of multiple layers of wrapping/conversion.
>
> The implementation used to be full of alternation between:
>
> interface Foo<T,U> {
>     U transform(T t);
> }
>
> class FooAdapter<T,U> {
>     FooAdapter(Function<T,U> lambda) { ... }
>
>     U transform(T t) { return lambda.apply(t); }
> }
>
> and
>
> Function<T,U> parentTransformer = foo::transform;
>
> and back again, introducing layers of wrapping even when the function 
> is not changing across layers.

Yes, the other problem is if we have something which is recursive,
we could easily end-up with a chain of adapters as long as the number of 
recursive calls.

This problem frequently arrives in dynamic language runtime when by 
example you convert from j.l.String to GroovyString and do the opposite 
operation. The only sane way to implement that is to provide a way to 
box and unbox things.
So having Collector being a triple seams to be the only sane choice.

Rémi

>
>
>
> On 2/8/2013 11:22 AM, Kevin Bourrillion wrote:
>> My subjective sense of good Java API design very strongly prefers the
>> "before" picture here, which I see as a lot more "Java-like", so I'm
>> taking a closer look.
>>
>> I assume that the trade-offs we're weighing here are purely to do with
>> what it's like to be a Collector implementor, correct?
>>
>>
>> On Fri, Feb 8, 2013 at 7:25 AM, Brian Goetz <brian.goetz at oracle.com
>> <mailto:brian.goetz at oracle.com>> wrote:
>>
>>     FYI: In a recent refactoring, I changed:
>>
>>     public interface Collector<T, R> {
>>          R makeResult();
>>          void accumulate(R result, T value);
>>          R combine(R result, R other);
>>     }
>>
>>     to
>>
>>     public interface Collector<T, R> {
>>          Supplier<R> resultSupplier();
>>          BiConsumer<R, T> accumulator();
>>          BinaryOperator<R> combiner();
>>     }
>>
>>     Basically, this is a refactoring from typical interface to
>>     tuple-of-lambdas.  What I found was that there was a lot of
>>     adaptation going on, where something would start out as a lambda,
>>     we'd wrap it with a Collector whose method invoked the lambda, then
>>     take a method reference to that wrapping method and then later wrap
>>     that with another Collector, etc.  By keeping access to the
>>     functions directly, the Collectors code got simpler and less wrappy,
>>     since a lot of functions could just be passed right through without
>>     wrapping.  And a lot of stupid adapter classes went away.
>>
>>     While clearly we don't want all interfaces to evolve this way, this
>>     is one where *all* the many layers of manipulations are effectively
>>     function composition, and exposing the function-ness made that
>>     cleaner and more performant.  So while I don't feel completely
>>     super-great about it, I think its enough of a win to keep.
>>
>>
>>
>>
>> -- 
>> Kevin Bourrillion | Java Librarian | Google, Inc. |kevinb at google.com
>> <mailto:kevinb at google.com>



More information about the lambda-libs-spec-experts mailing list