2016-04-29 86 views
34

我們知道Java 8引入了新的Stream API,並且java.util.stream.Collector是定義如何聚合/收集數據流的接口。爲什麼以這種方式設計Java 8'Collector'類?

然而,收藏家界面設計是這樣的:

public interface Collector<T, A, R> { 
    Supplier<A> supplier(); 
    BiConsumer<A, T> accumulator(); 
    BinaryOperator<A> combiner(); 
    Function<A, R> finisher(); 
} 

爲什麼沒有設計像下面?

public interface Collector<T, A, R> { 
    A supply(); 
    void accumulate(A accumulator, T value); 
    A combine(A left, A right); 
    R finish(A accumulator); 
} 

後者更容易實現。設計它作爲前者的考慮是什麼?

+0

是關於OOP和結構一致的,只是簡單的猜測。 – PSo

+3

請注意,您可以實現適應第二種模式的抽象基類。 – Thilo

+0

@Thilo我認爲第一和第二模式都可以在每個方向進行調整。我只是覺得第二個更直觀。 – popcorny

回答

26

實際上它最初的設計與你的建議相似。請參閱項目lambda存儲庫中的the early implementationmakeResult現在爲supplier)。這是後來updated到目前的設計。我相信,這種更新的基本原理是簡化收集器組合器。我沒有找到有關此主題的任何具體討論,但我的猜測得到了mapping收藏家出現在相同變更集中的事實的支持。考慮Collectors.mapping實現:

public static <T, U, A, R> 
Collector<T, ?, R> mapping(Function<? super T, ? extends U> mapper, 
          Collector<? super U, A, R> downstream) { 
    BiConsumer<A, ? super U> downstreamAccumulator = downstream.accumulator(); 
    return new CollectorImpl<>(downstream.supplier(), 
           (r, t) -> downstreamAccumulator.accept(r, mapper.apply(t)), 
           downstream.combiner(), downstream.finisher(), 
           downstream.characteristics()); 
} 

此實現需要重新定義只accumulator功能,留下suppliercombinerfinisher原樣,因此調用suppliercombinerfinisher當你沒有額外的間接:你只需要調用直接由原始收集器返回的函數。這是更重要的與collectingAndThen

public static<T,A,R,RR> Collector<T,A,RR> collectingAndThen(Collector<T,A,R> downstream, 
                  Function<R,RR> finisher) { 
    // ... some characteristics transformations ... 
    return new CollectorImpl<>(downstream.supplier(), 
           downstream.accumulator(), 
           downstream.combiner(), 
           downstream.finisher().andThen(finisher), 
           characteristics); 
} 

這裏只finisher改變,但是原來supplieraccumulatorcombiner被使用。由於每個元素都調用accumulator,所以減少間接性可能非常重要。嘗試用你提出的設計重寫mappingcollectingAndThen,你會看到問題。新的JDK-9收集器如filteringflatMapping也受益於當前的設計。

18

Composition is favored over inheritance.

在你的問題的第一個模式是不大不小的模塊配置的。收集器接口的實現可以爲供應商,累加器等提供不同的實現。這意味着可以構成現有的供應商,累加器等實現池中的收集器實現。這也有助於重複使用,兩個收集器可能會使用相同的累加器實現。 Stream.collect()使用提供的行爲。

在第二種模式中,收集器實現必須自行實現所有功能。所有類型的變體都需要覆蓋父實現。如果兩個收集器在一個步驟中具有相似的邏輯(例如,累積),則沒有太多的重用範圍和代碼重複。

+0

感謝您的回覆。但對於我的版本,通過這些lambda函數實現Collector.of(....)的幫助包裝版本也很容易。 – popcorny

0

2倍相關的原因

  • 功能組合物通過組合子。(注意你仍然可以做OO組成,但看下面點)時分配對象爲功能接口經由lambda表達式或方法引用在簡潔表達代碼成幀業務邏輯
  • 可能性。

    功能組合物

    收藏家API鋪平的方式用於通過組合子.i.e功能組合物。構建小/可重用的小功能,並將其中的一些經常以一種有趣的方式結合到高級特性/功能中。

    簡潔表達代碼

    下面我們使用函數指針(員工::的getSalary)填補映射器從Employee對象到INT的功能。 summingInt填充了添加ints的邏輯,因此將我們的工資總和寫在一行聲明代碼中。

    僱員 INT總= employees.stream() .collect的工資//計算總和(Collectors.summingInt(僱員::的getSalary)));

相關問題