2015-06-25 42 views
1

我正在使用Stream.reduce()进行试验,并且遇到了类型系统的问题。这里有一个玩具例子:使用通配符类型减少数据流

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

这适用于任何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 NumberNumber不同。但我不确定如何处理这种情况。我如何允许减少(或收集)子类的集合(例如List<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)); 
} 
+0

@SotiriosDelimanolis不直接,但伟大的建议!我会更多地发表一个答案。 – dimo414

+2

如果你的参数是一个BigDecimals列表,那么'(Number)0'是什么意思? – assylias

+0

@assylias取决于可能导致问题的累加器函数,但在这种情况下它不应该 - 我想要某种'Number',不一定是'BigDecimal'。 'Set'示例可能会澄清何时这可能有用。我想返回某种'Set',我不在乎它是什么实现。 – dimo414

回答

1

的问题实际上不是与? 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); 
} 

这是因为这两个通配符和有界方法不能确定身份的参数是相同的类型列表中的元素(除非它是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().reduce((Set<E>)ImmutableSet.<E>of(), 
    (a, b) -> Sets.union(a, b), 
    (a, b) -> Sets.union(a, b)); 
} 

有些烦人,你必须复制的还原功能,因为accumulatorcombiner是不同的类型。你大概可以在变量中定义一次,然后通过一个不安全的演员传递给它们,但我并不确定这是一种改进。

+1

您可以简化您的“设置解决方案”以避免完全投射,并且由于方法表达式而具有更短的语法: 'sets.stream()。 (ImmutableSet。(),Sets :: union,Sets :: union);' –