2016-05-25 6 views
4

JDKのダウンストリームリダクションの実装を理解しようとしています。ここでは、次のとおりです。//1ダウンストリームリダクションの実装の理解

public static <T, K, D, A, M extends Map<K, D>> 
    Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier, 
            Supplier<M> mapFactory, 
            Collector<? super T, A, D> downstream) { 
     Supplier<A> downstreamSupplier = downstream.supplier(); 
     BiConsumer<A, ? super T> downstreamAccumulator = downstream.accumulator(); 
     BiConsumer<Map<K, A>, T> accumulator = (m, t) -> { 
      K key = Objects.requireNonNull(classifier.apply(t), "element cannot be mapped to a null key"); 
      A container = m.computeIfAbsent(key, k -> downstreamSupplier.get()); 
      downstreamAccumulator.accept(container, t); 
     }; 
     BinaryOperator<Map<K, A>> merger = Collectors.<K, A, Map<K, A>>mapMerger(downstream.combiner()); 
     @SuppressWarnings("unchecked") 
     Supplier<Map<K, A>> mangledFactory = (Supplier<Map<K, A>>) mapFactory; 

     if (downstream.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)) { 
      return new CollectorImpl<>(mangledFactory, accumulator, merger, CH_ID); 
     } 
     else { 
      @SuppressWarnings("unchecked") 
      Function<A, A> downstreamFinisher = 
         (Function<A, A>) downstream.finisher(); //1, <------------- HERE 
      Function<Map<K, A>, M> finisher = intermediate -> { 
       intermediate.replaceAll((k, v) -> downstreamFinisher.apply(v)); 
       @SuppressWarnings("unchecked") 
       M castResult = (M) intermediate; 
       return castResult; 
      }; 
      return new CollectorImpl<>(mangledFactory, accumulator, merger, finisher, CH_NOID); 
     } 
    } 

downstreamFinisherはタイプFunction<A, D>です。タイプパラメータ宣言<T, K, D, A, M extends Map<K, D>>で判断すると、タイプDAに依存しません。では、なぜそれをFunction<A, A>にキャストしますか?私は、タイプDAのサブクラスではないかもしれないと思います。

私は何を欠席しましたか?

答えて

5

ダウンストリームコレクタに識別フィニッシャがなく、フィニッシャ関数が中間コンテナタイプと異なるタイプを返す場合、このコレクタは実際にMapのジェネリック型安全性に違反しています。

収集操作中、マップにはタイプAのオブジェクト、つまり中間コンテナタイプが保持されます。次に、操作の最後にgroupingByのフィニッシャがマップを通過し、フィニッシャ機能を各値に適用し、最終結果と置き換えます。

もちろん、これはチェックされていない操作では実装できません。複数の方法がありますが、投稿したバリアントによってSupplier<M>からSupplier<Map<K, A>>(最初のチェックされていない操作)のマップサプライヤのタイプが変更されるため、DではなくAの値がマップに保持されます。そのため、finisher関数をFunction<A,A>(2番目のチェックされていない操作)に変更する必要があります。実際にはDにもかかわらずAのオブジェクトが必要と思われるマップのreplaceAll操作で使用できます。最後に、結果マップは、M(3番目のチェックされていない操作)にキャストして、期待される結果タイプMのオブジェクトを取得する必要があります。

正しいタイプの安全な代替方法は、異なるマップを使用して、結果マップに中間マップの値を変換した結果を入力して仕上げ操作を実行することです。これは高価な操作であるだけでなく、提供されたサプライヤが最終結果に適したマップを作成するため、中間マップの2番目のサプライヤが必要になります。つまり、開発者はこれをタイプセーフの違反として許容することにしました。それは中間容器を入れしようとするため

Stream.of("foo", "bar").collect(Collectors.groupingBy(String::length, 
    () -> Collections.checkedMap(new HashMap<>(), Integer.class, Long.class), 
    Collectors.counting())); 

は、この実装でClassCastExceptionを生成します(:あなたは型の安全性が強制Map実装を使用しようとしたときに、危険な運転を気づくことができる

注意配列)をLongの代わりにマップに追加します。

関連する問題