Как написать метод объединения 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 не компилируют:
Eclipse (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>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 TCAP#2 extends Object super: T from capture of? super T
Но он должен работать: каждый подкласс Consumer также может использоваться как потребитель. И каждый потребитель супертипа X может также потреблять Xs. Я попытался добавить параметры типа в каждую строку потоковой версии, но это не поможет. Но если я записываю его с помощью традиционного цикла, он компилирует:
<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;
}
(Фильтрация нулевых значений оставлена для краткости.)
Поэтому мой вопрос: как я могу убедить JavaC и Eclipse в том, что мой код верен? Или, если это неверно: почему версия цикла корректна, но не версия Stream?