Почему в Java нет метода Stream.concat на уровне экземпляра?

Я знаю, что существует Stream.concat (док) для объединения двух потоков. Однако я сталкивался со случаями, когда мне нужно добавить «еще несколько» элементов в существующий поток, а затем продолжить его обработку. В такой ситуации я ожидал, что смогу объединить такие методы, как:

getStream(someArg)
  .map(Arg::getFoo)
  .concat(someOtherStreamOfFoos) // Or append, or...
  .map(...)

Однако такого метода цепочки append/concat на уровне экземпляра не существует.

Это не вопрос, требующий решения этой проблемы или более элегантных подходов (хотя я, конечно, был бы благодарен за любые другие точки зрения!). Скорее, я спрашиваю о факторах дизайна, которые привели к этому решению. Интерфейс Stream, как я полагаю, был разработан некоторыми чрезвычайно умными людьми, которые знают о Принцип наименьшего удивления, поэтому я должен предположить, что их решение опустить этот (для меня) интуитивно очевидный метод означает либо то, что метод является антипаттерном, либо что это невозможно из-за некоторых технических ограничений. Я бы хотел узнать причину.

Это хороший вопрос, но я пытаюсь придумать случай, когда сделать его методом экземпляра лучше, чем быть static. например, показанный вами фрагмент кода также может быть записан как Stream.concat(getStream(someArg).map(Arg::getFoo), someOtherStreamOfFoos).map(...)

Ousmane D. 02.04.2018 19:40

Я подозреваю, что это не сработало бы с системой типов.

user2357112 supports Monica 02.04.2018 19:41

@ Aominè - согласен, практически такая же функциональность определенно по-прежнему доступна существующими методами. Мой аргумент больше связан с удобочитаемостью - «чтение вниз» конвейера Stream с последовательными операциями фильтрации / сопоставления / сокращения для меня гораздо более интуитивно понятно, чем необходимость «перепрыгивать» на несколько строк (после того, как я заметил лишнее закрытие) чтобы идентифицировать concat, который был заявлен выше.

scubbo 02.04.2018 19:45

Хороший вопрос. Возможно, комментарий Use caution when constructing streams from repeated concatenation является его частью. Если бы это был беглый API, у вас может возникнуть соблазн многократно объединять, но вынуждаете вас это делать, например. concat(s1, concat(s2, s3)) делает людей менее склонными к этому?

Paul Boddington 02.04.2018 19:46

Я нашел набор изменений, который перемещает concat в статический метод: mail.openjdk.java.net/pipermail/lambda-dev/2012-November/…, но никаких комментариев относительно того, почему, похоже, не дано. Ни здесь: bugs.openjdk.java.net/browse/JDK-8015315

Jorn Vernee 02.04.2018 19:54

Я полагаю, что реализация конвейера Stream достаточно сложна, чтобы позволить использовать более одного источника для элементов. Я также не знаю, как параллелизм будет играть в этой ситуации. В библиотеке StreamEx есть методы append и prepend, так что это выполнимо. Однако я не осознаю сложностей.

fps 02.04.2018 21:05
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
12
6
533
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Я могу назвать вам одну причину, по которой это не сработало бы.

Stream.concat определяется как

static <T> Stream<T> concat(Stream<? extends T> a,
                            Stream<? extends T> b)

Вы можете concat, Stream<HashMap> и Stream<Map> в Stream<Map> или даже concat, Stream<HashMap> и Stream<TreeMap> в Stream<Map>. Чтобы сделать это с помощью метода экземпляра, вам нужно будет объявить параметр типа, например <U super T>, что в Java не разрешено.

// It'd look kind of like this, if Java allowed it.
public <U super T> Stream<U> concat(Stream<? extends U> other)

Java допускает только параметры типа с ограничением сверху, но не с ограничением снизу.

Объединение Stream<Something> и Stream<SomethingElse> может показаться необычным, но вывод типа часто дает параметры типа, слишком специфичные для работы с методом экземпляра. Например,

Stream.concat(Stream.of(dog), animalStream)

который потребовал бы явного параметра типа, если бы он был записан как

Stream.<Animal>of(dog).concat(animalStream)

Я думаю, что возвращение одного и того же универсального типа все равно будет полезно в большинстве случаев, например, когда два потока имеют один и тот же тип. В редких случаях они могут оставить менее удобную статическую версию. Кроме того, для тех, кому интересно, что Зачем<U super T> недействителен Java, см .: stackoverflow.com/a/33895470/2643425

Sean Van Gorder 02.04.2018 20:24

Если я не ошибаюсь, это возможно с помощью метода цепочки, описанного в bugs.openjdk.java.net/browse/JDK-8140283? Тогда это уменьшило бы проблему читабельности.

Koekje 02.04.2018 20:25

Это правда, но на практике это не имело бы значения. Если бы подпись была Stream<T> concat(Stream<? extends T> other), это было бы нормально для большинства случаев. В редких случаях, когда вы действительно хотите объединить Stream<Integer> и Stream<Double>, вы могли бы просто сделать integerStream.map(a -> (Number) a).concat(doubleStream).

Paul Boddington 02.04.2018 21:07

@PaulBoddington: Я подозреваю, что это будет иметь большее значение в случаях, когда первый аргумент также требует вывода типа, например Stream.concat(Stream.of(dog), animalStream).

user2357112 supports Monica 02.04.2018 21:36

Думаю, это просто упущенная функциональность Stream API.

Обратите внимание, что в Observable RxJava есть метод concatWith с требуемой функциональностью, поэтому ваш вопрос является разумным:

Observable<String> o1 = ...;
Observable<Integer> o2 = ...;
o1.map(s -> s.length())
  .concatWith(o2)
  ....

У Java 8 есть еще одна функциональность, которую приятно иметь - получить еще один необязательный, если текущий необязательный параметр пуст, например:

Optional.ofNullable(x).orElse(anotherOptional)

Я хочу сказать, что описанный вами concat возможен, но не реализован в Stream.

использование orElse(anotherOptional) неправильно. Либо это должен быть orElse(aValue), либо or(anotherOptional), представленный java9.

Lino 05.04.2018 11:02

Другие вопросы по теме