使用通配符类型减少流

时间:2015-06-25 04:44:08

标签: java generics java-8 java-stream

我正在试验Stream.reduce(),并且遇到了类型系统的问题。这是一个玩具示例:

List<Number>

这适用于任何? extends Number,但如果我希望能够减少public static Number reduceNum2(List<? extends Number> nums) { return nums.stream().reduce((Number)0, (a, b) -> a); } 的列表,该怎么办?这不会编译:

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

错误:

Stream.reduce()

从概念上讲,我知道为什么会这样。 ? extends Number必须返回与源代码相同类型的内容,NumberList<Integer>不同。但我不确定如何处理这种情况。如何减少(或收集)子类的集合(例如public static <E> Set<E> reduceSet1(List<? extends Set<E>> sets) { return sets.stream().reduce(ImmutableSet.<E>of(), (a, b) -> Sets.union(a, b)); } )?

如果有帮助,这是一个更实际的例子,同样无法编译:

 byte[] utcs2Bytes = System.Text.Encoding.Convert(System.Text.Encoding.UTF8, System.Text.Encoding.Unicode, newArray);

2 个答案:

答案 0 :(得分:2)

问题实际上不在? extends,而在identity参数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);
}

这是因为通配符和有界方法都无法确定identity参数是否与列表元素的类型相同(除非它是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> Set<E> reduceSet2(List<? extends Set<E>> sets) {
  return sets.stream().<Set<E>>reduce(
      ImmutableSet.<E>of(), Sets::union, Sets::union);
}

由于accumulatorcombiner是不同的类型,因此您必须复制缩减功能。你可能会在一个变量中定义它一次并通过一个不安全的演员将它传递给两者,但我不确定这是一个改进。

答案 1 :(得分:1)

我遇到了类似的问题,并且设法解决了以下问题

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