Java8泛型:将消费者流减少为单个消费者

Java8泛型:将消费者流减少为单个消费者,java,generics,java-8,java-stream,Java,Generics,Java 8,Java Stream,如何使用消费者编写一种方法,将消费者的流组合成单个消费者。然后(消费者) 我的第一个版本是: <T> Consumer<T> combine(Stream<Consumer<T>> consumers) { return consumers .filter(Objects::nonNull) .reduce(Consumer::andThen) .orElse(noOp

如何使用
消费者编写一种方法,将
消费者的
组合成单个
消费者
。然后(消费者)

我的第一个版本是:

<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 */ };
}
这是不可能编译的。改进后的版本将是:

<T> Consumer<T> combine(Stream<? extends Consumer<? super T>> consumers) {
    return consumers
            .filter(Objects::nonNull)
            .reduce(Consumer::andThen)
            .orElse(noOpConsumer());
}
(为了简洁起见,没有过滤空值。)


因此,我的问题是:如何让JavaC和Eclipse相信我的代码是正确的?或者,如果它不正确:为什么循环版本正确而流版本不正确?

您忘记了方法定义中的一件小事。目前是:

<T> Consumer<T> combine(Stream<? extends Consumer<? super T>> consumers) {}
现在应该可以使用了

您使用的是具有以下签名的单参数版本:

Optional<T> reduce(BinaryOperator<T> accumulator);
我建议您使用该方法的三参数版本:

<U> U reduce(U identity,
             BiFunction<U, ? super T, U> accumulator
             BinaryOperator<U> combiner);
U减少(U标识,
双功能累加器
二进制运算符组合器);
双功能累加器
可以接受两种不同类型的参数,限制较少,更适合您的情况。一个可能的解决办法是:

<T> Consumer<T> combine(Stream<? extends Consumer<? super T>> consumers) {
    return consumers.filter(Objects::nonNull)
                    .reduce(t -> {}, Consumer::andThen, Consumer::andThen);
}

Consumer combine(Stream如果您有很多消费者,则应用
Consumer.and()
将创建一个巨大的消费者包装树,递归处理该包装树以调用每个原始消费者

因此,简单地构建一个消费者列表,并创建一个简单的消费者来对其进行迭代可能更有效:

<T> Consumer<T> combine(Stream<? extends Consumer<? super T>> consumers) {
    List<Consumer<? super T>> consumerList = consumers
            .filter(Objects::nonNull)
            .collect(Collectors.toList());
    return t -> consumerList.forEach(c -> c.accept(t));
}

循环版本如何允许返回
消费者
?@user7抱歉,但我不理解您的问题。您是否询问当前如何可能(以及原因)。或者如何修复它,使其成为可能?类型转换为
(Consumer@user7我现在明白了,但这是不是需要的?为什么你首先要返回
消费者
?如果你想返回一个无限制的消费者,你可以简单地用
(消费者)
再次强制转换它,这是未选中的,但会起作用。不能
c->c.accept(t)
是否替换为
Consumer::accept
?@Alexander No,
forEach
需要一个Consumer,即接受一个参数但不返回任何内容。
Consumer::accept
需要两个参数:
Consumer
本身(“
this
”)以及要消费的对象。事实上,你可以很容易地注意到它并不等价,因为
t
将与该方法引用一起变得不可用。哦,是的,这使得sense@Alexander还要注意的是,
forEach
实际上需要一个
消费者
<? extends Consumer<? super T>>
<U> U reduce(U identity,
             BiFunction<U, ? super T, U> accumulator
             BinaryOperator<U> combiner);
<T> Consumer<T> combine(Stream<? extends Consumer<? super T>> consumers) {
    return consumers.filter(Objects::nonNull)
                    .reduce(t -> {}, Consumer::andThen, Consumer::andThen);
}
<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);
<T> Consumer<T> combine(Stream<? extends Consumer<? super T>> consumers) {
    List<Consumer<? super T>> consumerList = consumers
            .filter(Objects::nonNull)
            .collect(Collectors.toList());
    return t -> consumerList.forEach(c -> c.accept(t));
}
return t -> consumers
        .filter(Objects::nonNull)
        .forEach(c -> c.accept(t));