Stream.flatMap reference ambiguity
Dan Smith
daniel.smith at oracle.com
Tue Feb 26 10:59:16 PST 2013
On Feb 26, 2013, at 11:09 AM, Maurizio Cimadamore <maurizio.cimadamore at oracle.com> wrote:
> Now - in this example there are three potentially applicable methods
> (after provisional applicability):
>
> 1) <R> Stream<R> flatMap(FlatMapper<? super T, R> mapper) // (T, Consumer<R>)void
> 2)IntStream flatMap(FlatMapper.ToInt<? super T> mapper); // (int, IntConsumer)void
> 3)LongStream flatMap(FlatMapper.ToLong<? super T> mapper); // (long, LongConsumer)void
> 4)DoubleStream flatMap(FlatMapper.ToDouble<? super T> mapper); // (double, DoubleConsumer)void
>
>
>
> the first parameter type inferred from the descriptor is interesting: it
> can be either T (the type of the Stream, String in this case), int, long
> or double. But if you look at the code, it contains this snippet:
>
> x.split(",")
>
>
> This will only compile when x has type String - which means only method
> (1) is applicable, no ambiguity should be issued.
Correct. The invocation of "split" should disambiguate (as well as the invocation of 'accept' with a String).
I think the core problem is the API (in general, it's best to avoid overloading with functional interfaces that have different parameter types), but this is an interesting example in any case.
David, this is actually something we've had a hard time predicting user expectations on. You get the more powerful inference, but at the cost of more fragile code. As a programmer, are you okay with that? (Other interested parties are free to chime in, too.)
Here's the original example, which should compile:
List<String> ss = new ArrayList<>();
ss.stream().flatMap((x, sink) -> {
for (String s : x.split(",")) {
sink.accept(s);
}
});
If I refactor (maybe for debugging) to something like this, it won't compile anymore, due to ambiguity:
List<String> ss = new ArrayList<>();
ss.stream().flatMap((x, sink) -> sink.accept(x));
If I add some logging code, it might compile again:
List<String> ss = new ArrayList<>();
ss.stream().flatMap((x, sink) -> {
if (x.startsWith("a")) System.out.println(x);
sink.accept(x));
});
The key is that the first example uses methods that make it clear that 'x' is a String (or that 'sink' is not an IntConsumer/LongConsumer/DoubleConsumer).
—Dan
More information about the lambda-dev
mailing list