どのようにして組み合わせる方法書くことができるStream
のConsumers
シングルにConsumer
使用してConsumer.andThen(Consumer)
?
私の最初のバージョンは:
<T> Consumer<T> combine(Stream<Consumer<T>> consumers) {
return consumers
.filter(Objects::nonNull)
.reduce(Consumer::andThen)
.orElse(noOpConsumer());
}
<T> Consumer<T> noOpConsumer() {
return value -> { /* do nothing */ };
}
このバージョンでは、javacのとEclipseでコンパイルされます。しかし、それはあまりにも具体的です:Stream
することはできませんStream<SpecialConsumer>
、とあればConsumers
正確にタイプのではありませんT
が、それのスーパータイプ、それを使用することはできません。
Stream<? extends Consumer<? super Foo>> consumers = ... ;
combine(consumers);
それは当然、コンパイルされません。改良版は、次のようになります。
<T> Consumer<T> combine(Stream<? extends Consumer<? super T>> consumers) {
return consumers
.filter(Objects::nonNull)
.reduce(Consumer::andThen)
.orElse(noOpConsumer());
}
しかし、Eclipseのものjavacのコンパイルでもないこと:
エクリプス(4.7.3a):
タイプが
Consumer
定義されていないandThen(capture#7-of ? extends Consumer<? super T>, capture#7-of ? extends Consumer<? super T>)
ことがここで適用されます
javacの(1.8.0172):
エラー:互換性のない型:無効なメソッド参照
.reduce(Consumer::andThen)
互換性のない型は:Consumer<CAP#1>
に変換できないConsumer<? super CAP#2>
場合T
、型の変数である:
T extends Object
方法で宣言された<T>combine(Stream<? extends Consumer<? super T>>)
場合CAP#1
、CAP#2
新鮮な型変数です。
CAP#1 extends Object super: T from capture of ? super T
CAP#2 extends Object super: T from capture of ? super T
しかし、それは動作するはずです:消費者のすべてのサブクラスでは、あまりにも、消費者として使用することができます。そして、Xのスーパータイプのすべての消費者は、あまりにも、Xを消費することができます。私は、ストリームバージョンの各ラインに型パラメータを追加しようとしましたが、それは助けにはなりません。私は伝統的なループでそれを書き留めた場合でも、それがコンパイルされます。
<T> Consumer<T> combine(Collection<? extends Consumer<? super T>> consumers) {
Consumer<T> result = noOpConsumer()
for (Consumer<? super T> consumer : consumers) {
result = result.andThen(consumer);
}
return result;
}
(NULL値をフィルタリングすることは簡潔にするために省略されます。)
そこで、私の質問は:どのように私は私のコードが正しいことのjavacとEclipseを納得させることができますか?それが正しくない場合や、なぜループのバージョンは正しいがないStream
バージョン?
あなたは一引数の使用Stream.reduce(accumulator)
次のシグネチャを持つバージョン:
Optional<T> reduce(BinaryOperator<T> accumulator);
BinaryOperator<T> accumulator
唯一のタイプの要素を受け入れることができますT
が、あなたは持っています:
<? extends Consumer<? super T>>
私はあなたの3引数バージョンを使用することを提案するStream.reduce(...)
代わりの方法を:
<U> U reduce(U identity,
BiFunction<U, ? super T, U> accumulator
BinaryOperator<U> combiner);
BiFunction<U, ? super T, U> accumulator
2種類の異なるパラメータを受け入れることができ、バインド制限の少ないを持っており、自分の状況に適しています。可能な解決策は次のようになります。
<T> Consumer<T> combine(Stream<? extends Consumer<? super T>> consumers) {
return consumers.filter(Objects::nonNull)
.reduce(t -> {}, Consumer::andThen, Consumer::andThen);
}
第三引数はBinaryOperator<U> combiner
唯一の並列ストリームで呼び出されますが、いずれにせよ、それの正しい実装を提供するのが賢明だろう。
次のように加えて、より良く理解いずれかの上記のコードを表すことができます。
<T> Consumer<T> combine(Stream<? extends Consumer<? super T>> consumers) {
Consumer<T> identity = t -> {};
BiFunction<Consumer<T>, Consumer<? super T>, Consumer<T>> acc = Consumer::andThen;
BinaryOperator<Consumer<T>> combiner = Consumer::andThen;
return consumers.filter(Objects::nonNull)
.reduce(identity, acc, combiner);
}
今、あなたは書くことができます。
Stream<? extends Consumer<? super Foo>> consumers = Stream.of();
combine(consumers);