Как написать метод объединения 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 T
CAP#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
?