使用通配符类型减少数据流 [英] Stream reduction with wildcard types
问题描述
我正在试验 Stream.reduce()
,并且遇到了类型系统的障碍。这里有一个玩具的例子:
public static Number reduceNum1(List< Number> nums){
时才可以。
return nums.stream ).reduce(0,(a,b) - > a);
$ b这适用于任何
List< Number>
,但是如果我希望能够减少的列表呢?扩展Number
?这不会编译:
public static Number reduceNum2(List< ;? extends Number> nums){
return nums ((数字)0,(a,b) - > a);
}
出现错误:
ReduceTest.java:72:error:找不到适合reduce的方法(Number,(a,b) - > a)
返回nums.stream()。 ((数量)0,(a,b)→a);
$
方法Stream.reduce(CAP#1,BinaryOperator& CAP#1>)不适用
(参数不匹配;数字不能转换为CAP#1)
方法(U,BiFunction< U,?super CAP#1,U>,BinaryOperator< U>)不适用
(不能推断类型变量U
实际和形式参数列表的长度不同))
其中U,T是类型变量:
U extends在方法< U>中声明的对象reduce(U,BiFunction< U,?super T,U> ;,BinaryOperator< U>)
T扩展在接口Stream
中声明的对象,其中CAP#1是一个新的类型变量:
CAP#1扩展Number of capture from?扩展Number
注意:有些消息已被简化;使用-Xdiags:verbose重新编译以获得完整输出
从概念上讲,我得到了这种情况。
Stream.reduce()
必须返回与源元素相同类型的东西,而? extends Number
与Number
不一样。但我不确定如何处理这种情况。我如何允许减少(或收集)一个子类的集合(例如List< Integer>
)?
如果有帮助,下面是一个更实用的例子,它同样不能编译:
public static< E>设置< E - 代替; (),(a,b) - > Sets.union()(set
> set a,b));
}
解决方案与
?扩展
,但使用标识
参数为reduce()
。正如Sotirios Delimanolis建议的那样,您可以指定一个有界类型N extends Number
,但只有当标识值<null public static< N extends Number> N reduceNum3(List N nums){
return nums.stream()。reduce(null,(a,b) - > a);
$ p这是因为通配符和有界方法都不能确定标识参数是否会与列表元素的类型相同(除非它是
null
,所有类型共享)。
解决方法是使用三参数
reduce()
方法,它允许您将结果视为不同的类型即使它不是 )。
以下是
Number
示例:public static Number reduceNum4(List< ;? extends Number> nums){
return nums.stream()。reduce((Number )0,
(a,b) - > a,
(a,b) - > a);
}
以下是
Set
例子:
public static< E>设置< E - 代替; (),
(a)的集合E(x)的集合E(x)的集合E(x) ,b) - > Sets.union(a,b),
(a,b) - > Sets.union(a,b));
$ b有点烦人的是,你必须重复约简函数,因为
accumulator
和组合器
是不同的类型。你大概可以在变量中定义一次,然后通过不安全的演员传递给它们,但我不确定这是一个进步。I'm experimenting with
Stream.reduce()
, and have run into a snag with the type system. Here's a toy example: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 be able to reduce a list that? extends Number
? This doesn't compile:public static Number reduceNum2(List<? extends Number> nums) { return nums.stream().reduce((Number)0, (a, b) -> a); }
With the 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 get, conceptually, why this happens.
Stream.reduce()
must return something of the same type as the source's elements, and? extends Number
isn't the same asNumber
. But I'm unsure how to handle this case. How can I allow collections of a subclass (e.g.List<Integer>
) to be reduced (or collected)?
If it helps, here's a more practical example that similarly fails to compile:
public static <E> Set<E> reduceSet1(List<? extends Set<E>> sets) { return sets.stream().reduce(ImmutableSet.<E>of(), (a, b) -> Sets.union(a, b)); }
解决方案The issue isn't actually with the
? extends
, but with theidentity
parameter toreduce()
. As Sotirios Delimanolis suggested, you can specify a bounded typeN extends Number
, but only if the identity value isnull
.public static <N extends Number> N reduceNum3(List<N> nums) { return nums.stream().reduce(null, (a, b) -> a); }
This is because both the wildcard and bounded methods cannot determine if the identity parameter will be the same type as the list's elements (unless it's
null
, which all types share).The workaround is to use the three-argument
reduce()
method, which allows you to treat the result as a different type (even if it's not really).Here's the
Number
example:public static Number reduceNum4(List<? extends Number> nums) { return nums.stream().reduce((Number)0, (a, b) -> a, (a, b) -> a); }
And here's the
Set
example:public static <E> Set<E> reduceSet2(List<? extends Set<E>> sets) { return sets.stream().reduce((Set<E>)ImmutableSet.<E>of(), (a, b) -> Sets.union(a, b), (a, b) -> Sets.union(a, b)); }
Somewhat annoyingly you have to duplicate the reduction function since the
accumulator
and thecombiner
are different types. You presumably could define it once in a variable and pass it to both via an unsafe cast, but I'm not really certain that's an improvement.这篇关于使用通配符类型减少数据流的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!