使用通配符类型减少数据流 [英] Stream reduction with wildcard types

查看:104
本文介绍了使用通配符类型减少数据流的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在试验 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 as Number. 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 the identity parameter to reduce(). As Sotirios Delimanolis suggested, you can specify a bounded type N extends Number, but only if the identity value is null.

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 the combiner 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆