2015-06-25 41 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);' –