Decrease flow using wildcards

I am experimenting with Stream.reduce() and ran into a type system problem. Here is an example of a toy:

 public static Number reduceNum1(List<Number> nums) { return nums.stream().reduce(0, (a, b) -> a); } 

This works for any List<Number> , but what if I want to shorten the list, which ? extends Number ? extends Number ? This does not compile:

 public static Number reduceNum2(List<? extends Number> nums) { return nums.stream().reduce((Number)0, (a, b) -> a); } 

With an error:

 ReduceTest.java:72: error: no suitable method found for reduce(Number,(a,b)->a) return nums.stream().reduce((Number)0, (a, b) -> a); ^ method Stream.reduce(CAP#1,BinaryOperator<CAP#1>) is not applicable (argument mismatch; Number cannot be converted to CAP#1) method Stream.<U>reduce(U,BiFunction<U,? super CAP#1,U>,BinaryOperator<U>) is not applicable (cannot infer type-variable(s) U (actual and formal argument lists differ in length)) where U,T are type-variables: U extends Object declared in method <U>reduce(U,BiFunction<U,? super T,U>,BinaryOperator<U>) T extends Object declared in interface Stream where CAP#1 is a fresh type-variable: CAP#1 extends Number from capture of ? extends Number Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output 

I understand why this is happening. Stream.reduce() should return something like the same type as the source, and ? extends Number ? extends Number does not match Number . But I'm not sure how to handle this. How can I reduce (or assemble) subclasses (e.g. List<Integer> )?


If this helps, here is a more practical example that won't compile in the same way:

 public static <E> Set<E> reduceSet1(List<? extends Set<E>> sets) { return sets.stream().reduce(ImmutableSet.<E>of(), (a, b) -> Sets.union(a, b)); } 
+2
java generics java-8 java-stream
source share
2 answers

The problem is not really with ? extends ? extends ? extends ? extends , but with the identity parameter to reduce() . As Sotirios Delimanolis suggested, you can specify a limited type of N extends Number , but only if the identifier value is null .

 public static <N extends Number> N reduceNum3(List<N> nums) { return nums.stream().reduce(null, (a, b) -> a); } 

This is due to the fact that both wildcard and restricted methods cannot determine whether an identity parameter will be of the same type as the list items (if it is not null , which all types share).

To eliminate this problem, use the three-argument reduce() method, which allows you to consider the result as a different type (even if it is not really).

Here is a Number example:

 public static Number reduceNum4(List<? extends Number> nums) { return nums.stream().reduce((Number)0, (a, b) -> a, (a, b) -> a); } 

And here is the Set example:

 public static <E> Set<E> reduceSet2(List<? extends Set<E>> sets) { return sets.stream().<Set<E>>reduce( ImmutableSet.<E>of(), Sets::union, Sets::union); } 

It’s a little annoying that you have to duplicate the reduction function, since accumulator and combiner different types. You could probably define it once in a variable and pass it to both through an unsafe cast, but I'm not quite sure if this is an improvement. Using method references is probably the right way.

+2
source share

I had a similar problem and managed to solve it as follows

 public static Number reduceNum2(List<? extends Number> nums) { return nums.stream().map(num->(Number) num).reduce(0, (a, b) -> a); } 
+1
source share

All Articles