Javaのジェネリック8:単一の消費者に消費者の流れを削減

user194860:

どのようにして組み合わせる方法書くことができるStreamConsumersシングルに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#1CAP#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バージョン?

オレクサンドルPyrohov:

あなたは一引数の使用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> accumulator2種類の異なるパラメータを受け入れることができ、バインド制限の少ないを持っており、自分の状況に適しています。可能な解決策は次のようになります。

<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);

おすすめ

転載: http://43.154.161.224:23101/article/api/json?id=37833&siteId=1