Replace RowOperation.initialValue() and rowAggregator() by collect(Collector)

Douglas Surber douglas.surber at
Wed Oct 25 15:29:23 UTC 2017


I like this suggestion. I will definitely look into it.

I agree about the type witnesses. I’m hoping that a future improvement in type inferencing will eliminate the need. 

The collect method does not eliminate the need for the type witness completely. The type inferencer cannot determine the type of the RowOperation from it. There are some special cases where replacing initialValue/rowAggregator with collect does eliminate the need for the type witness, but there are many other cases where it doesn’t help. Unfortunately the limitation of the type inferencer necessitate the type witness in some cases. My approach has been to always write the type witness; it needs to idiomatic for this kind of heavily typed fluent API.


> On Oct 25, 2017, at 7:17 AM, Lukas Eder <lukas.eder at> wrote:
> In this email, I will make two suggestions:
> - Replacing initialValue() and rowAggregator() by collect(Collector)
> - Improving the API to avoid the need for unidiomatic type witnesses
> While playing around with the ADBA API, it struck me that the
> RowOperation.initialValue() and rowAggregator() methods are not as
> versatile as they could be. In particular, the mechanism reminded me of the
> API where:
> - RowOperation.initialValue() corresponds to Collector.supplier()
> - RowOperation.rowAggregator() corresponds to Collector.accumulator()
> And these two Collector methods don't have any correspondence in ADBA:
> - Collector.combiner()
> - Collector.finisher()
> If we think of this "row aggregation" as an act of collecting all rows into
> a target type, we might be able to find some more interesting features
> where the Stream API and the new ADBA API can work together, even if ADBA
> is asynchronous, push-oriented and Stream is synchronous, pull-oriented.
> So, we could have: <A, R> RowOperation<R> collect(Collector<? super Row, A,
> R>)
> Note that I made this method suggestion generic as one thing that has
> always bothered me in the examples were the not-so-idiomatic type witnesses
> in examples like this one:
> conn
>    .<List<Integer>>rowOperation(sql)
>    .set("target", 42, JdbcType.NUMERIC)
>    .initialValue( () -> new ArrayList<>() )
>    .rowAggregator( (list, row) -> {
>        list.add(row.get("id", Integer.class));
>        return list;
>    } )
>    .submit();
> We could have, instead:
> conn
>    .rowOperation(sql)
>    .set("target", 42, JdbcType.NUMERIC)
>    .collect(Collector.of(
>        () -> new ArrayList<Integer>(),
>        (list, row) -> { ... }, // unchanged
>        (list1, list2) -> { ... }
>    ))
>    .submit();
> I understand that there isn't a complete match between the two paradigms.
> In particular, the Collector.combiner() doesn't make much sense in the
> absence of parallelism, but the Collector.finisher() could be quite useful
> in some situations where the intermediate, mutable accumulator is something
> like int[], i.e. not a type that we want to return to the calling operation.
> Thoughts?

More information about the jdbc-spec-discuss mailing list