Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why I'm getting Stream<Object> when I call stream() after collect()?

Consider the following example, which is not compiled:

List<Integer> list = Arrays.asList(1, 2, -3, 8);

        list.stream()
                .filter(x -> x > 0)
                .collect(ArrayList::new, ArrayList::add, ArrayList::addAll)
                .stream() // Stream<Object>
                .map(x -> x * 2)
                .forEach(System.out::println);

If I replace

.collect(ArrayList::new, ArrayList::add, ArrayList::addAll)

with

.collect(Collectors.toList())

The code will be compiled. So the question is how do I write collect() with supplier and accumulator(I need it) to be able to call a stream() after it?

like image 607
chill appreciator Avatar asked Sep 27 '18 17:09

chill appreciator


1 Answers

It appears that you've made a raw method reference to the ArrayList constructor with ArrayList::new.

The type argument was not inferred with:

.collect(ArrayList::new, ArrayList::add, ArrayList::addAll)

The 3-argument overload of collect expects 3 arguments, the first one being a Supplier<R>. At this point there is no connection between the collect method's type argument R and T which is Integer here. The only way to infer this would be through the second argument, a BiConsumer<R, ? super T>. Here you have ArrayList::add, which gives the compiler no way to infer R either.

You must supply what R is in the first argument, the Supplier. You can supply explicit type arguments to the class to create on a method reference.

.collect(ArrayList<Integer>::new, ArrayList::add, ArrayList::addAll)

This compiles, and the output is as expected:

2
4
16

When you use Collectors.toList(), you are supplying only one argument.

public static <T> Collector<T,?,List<T>> toList()

Here, there is only one type argument T, so the compiler can correctly infer that this T is Integer, so a List<Integer> is created, allowing the code to compile. The type arguments to the Collector returned bind T to List<T>, allowing the compiler to perform the type inference.

Note that this is only necessary in the first place because there is no target type to help with type inference; you continue with the stream manipulation and just call System.out.println at the end, which can take an Object.

If you had this code:

List<Integer> modified = list.stream()
            .filter(x -> x > 0)
            .collect(ArrayList::new, ArrayList::add, ArrayList::addAll);

Then target type inference would have supplied Integer for the type argument to ArrayList::new. This compiles also.

like image 158
rgettman Avatar answered Oct 06 '22 07:10

rgettman