2015-02-16 1 views
6

До Java 8 свойство, представляющее коллекцию элементов, обычно возвращает коллекцию. При отсутствии неизменного интерфейса сбора, общий идиом будет обернуть его как:Можно ли заменить Collection for Stream в возвращаемых значениях?

Collection<Foo> getFoos(){ return Collections.unmodifiableCollection(foos); } 

Теперь, когда поток здесь, заманчиво, чтобы начать подвергать Streams вместо коллекций.

Преимущества, как я их вижу:

  1. Действительно неизменны API
  2. Чаще всего, клиент такого свойства заинтересован в запросе или итерация результата (Это было бы действительно ужасно, если он хотел внести обновления в коллекцию ..).

С другой стороны, Streams можно употреблять только один раз, и не может передаваться как обычные коллекции. Это особенно тревожно.

Этот вопрос отличается от similar looking question, поскольку он более широк в том смысле, что ОП там явно заявляет, что потоки, которые он намерен вернуть, не будут переданы. На мой взгляд, этот аспект не рассматривался в ответах на исходный вопрос.

Другими словами: мне кажется, что если API возвращает поток, общее мышление должно состоять в том, что все взаимодействие с ним должно заканчиваться в непосредственном контексте. Запрещается пропускать поток вокруг.

Но, похоже, это очень сложно обеспечить, если разработчики не знакомы с Stream API. Это означает, что для такого типа API требуется сдвиг парадигмы. Правильно ли я об этом утверждении?

+0

Пожалуйста, подумайте о повторном открытии вопроса - я объяснил, почему я думаю, что это имеет значение. – Vitaliy

ответ

3

Позвольте мне предложить простое правило:

Stream, который передается в качестве аргумента метода или возвращается как возвращаемое значение метода должен быть хвост из несогласованного трубопровода.

Это, вероятно, так очевидно для тех из нас, кто работал над потоками, которые мы никогда не удосужились записать.Но это, вероятно, не очевидно для людей, приближающихся к потокам в первый раз, поэтому, вероятно, стоит обсудить.

Главное правило распространяется на Streams API package documentation: поток может иметь не более одной операции терминала. Как только это будет прекращено, запрещается добавлять какие-либо промежуточные или терминальные операции.

Другое правило состоит в том, что поточные трубопроводы должны быть линейными; у них не может быть филиалов. Это не очень четко документировано, но упоминается в Stream class documentation о двух третях пути вниз. Это означает, что незаконно добавлять промежуточную или терминальную операцию в поток, если это не последняя операция в конвейере.

Большинство методов потока являются промежуточными или терминальными операциями. Если вы попытаетесь использовать один из них в потоке, который завершен или это не последняя операция, вы довольно быстро узнаете, получив IllegalArgumentException. Иногда это случается, но я думаю, что когда люди получат идею о том, что конвейер должен быть линейным, они учатся избегать этой проблемы, и проблема уходит. Я думаю, что это довольно легко для большинства людей понять; он не должен требовать сдвига парадигмы.

Как только вы это понимаете, ясно, что если вы собираетесь передать экземпляр Stream другому фрагменту кода - либо передав его в качестве аргумента, либо вернув его вызывающему абоненту - он должен быть источника потока или последней промежуточной операции в конвейере. То есть, это должен быть хвост нескончаемого трубопровода.

Другими словами: мне кажется, что если API возвращает поток, общее мышление должно состоять в том, что все взаимодействие с ним должно заканчиваться в непосредственном контексте. Запрещается пропускать поток вокруг.

Я думаю, что это слишком ограничительный. Пока вы придерживаетесь правила, которое я предлагал, вы должны быть свободны передавать поток вокруг столько, сколько хотите. Действительно, существует множество вариантов использования для получения потока откуда-то, его изменения и передачи. Вот несколько примеров.

1) Откройте текстовый файл, содержащий текстовое представление POJO на каждой строке. Позвоните по телефону File.lines(), чтобы получить Stream<String>. Сопоставьте каждую строку с экземпляром POJO и возвращайте вызывающему абоненту Stream<POJO>. Вызывающий может применить фильтр или операцию сортировки и вернуть поток своему вызывающему.

2) Учитывая Stream<POJO>, вы можете иметь веб-интерфейс, позволяющий пользователю предоставлять комплексный набор критериев поиска. (К примеру, рассмотрим торговый сайт с большим количеством сортировки и параметров фильтрации.) Вместо составления большого сложного трубопровода в коде, вы можете иметь метод, как следующие:

Stream<POJO> applyCriteria(Stream<POJO>, SearchCriteria) 

который бы поток, применять критерии поиска, добавляя различные фильтры и, возможно, сортировать или выполнять отдельные операции и возвращать полученный поток вызывающему.

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

+0

Правила, которые вы предлагаете, абсолютно разумны, проблема в том, что это увеличивает психологическую нагрузку. Раньше, когда все просто работали с коллекциями, разработчикам никогда не приходилось думать дважды, прежде чем вызывать метод. Им не нужно было осторожно шагать по нему.Более того, часто бывает, что если задана последовательность, удобнее пересечь ее несколько раз на несколько строк кода. Теперь, казалось бы, безобидные операции внезапно становятся незаконными и по причинам, которые не сразу очевидны. Это относится к нашей предыдущей переписке о разовом характере потоков. (next comnt) – Vitaliy

+0

В мире, где все знают об этом, и разработчики не устают и всегда уделяют пристальное внимание тому, что они пишут, - это можно снять. Но я чувствую, что в нашей повседневной реальности это рецепт для раздражающих ошибок или, что еще хуже, новых идиом, которые проходят через средство регенерации потока в каждой точке для достижения такой функциональности. Или даже оберточные потоки, которые просто компенсируют некорректную природу потоков, что противоречит языковым намерениям. Sturat, есть ли способ, которым мы могли бы общаться более прямым образом (электронная почта?), Я считаю, что эта платформа слишком ограничена .. – Vitaliy

+0

Я создал чат для этого: http://chat.stackoverflow.com/rooms/72090/return-collection-or-stream –

0

Это зависит:

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

Использование потоков в ваших приложениях API увеличит вероятность того, что пользователи вашего приложения также будут передавать потоки вместо коллекций, что подразумевает, что им также необходимо помнить, что они не должны возвращать уже закрытые потоки.

В частных проектах с использованием Streams, вероятно, будет работать, но если вы создаете публичный API, я бы не стал рассматривать Streams как хорошую идею.

Лично я предпочитаю использовать Iterables в пользу Коллекций из-за их неизменности. Я создал обертку под названием Enumerables, чтобы расширить Iterable с помощью аналогичного функционального API, который имеет Stream.