diff --git a/lessons/java-core/050/Introduction to Stream API.md b/lessons/java-core/050/Introduction to Stream API.md new file mode 100644 index 0000000..9732fb0 --- /dev/null +++ b/lessons/java-core/050/Introduction to Stream API.md @@ -0,0 +1,264 @@ +# Немного о Stream API. И много о смежной терминологии + +Этот и следующие уроки будут в разной степени посвящены одной практической и весьма важной в современной Java-разработке +теме – Stream API. + +## Stream API для самых маленьких + +На разных этапах погружения мы будем рассматривать этот инструмент под разными углами, давая разные определения. Пока же +остановимся на простой аналогии: + +**Stream API** – механизм, подобный `Optional`, предназначенный для обработки массива (в широком смысле) данных одного +типа. + +Пример использования Stream API мы рассматривали +в [уроке 48](https://telegra.ph/Funkcionalnoe-programmirovanie-v-Java-02-14). + +В рамках ближайших уроков мы убедимся, что функциональность Stream’ов намного шире, чем функциональность `Optional`. +Пока же ограничимся тем, что внешне они очень похожи: выглядят как цепочки функциональных вызовов, операции над ними (их +методы) можно разделить на промежуточные и терминальные, появляются в результате «порождающих» операций. + +Более того, некие промежуточные операции (`map()`, `flatMap()`, `filter()`) у `Stream` и `Optional` внешне одинаковы, а +значит, мы уже сейчас можем решать несложные задачи с помощью Stream API, чем и займемся в практической части урока. + +Как можно догадаться, взаимодействие со Stream API строится на интерфейсе `Stream`, расположенном в +пакете `java.util.stream`. Мы подробно ознакомимся с этим интерфейсом и его предком в дальнейшем. Однако для выполнения +практической части вам, вероятно, придется заглянуть туда самостоятельно или хотя бы попытаться использовать методы, +которые там находятся. + +В качестве подспорья для практической части и демонстрации примеров использования, предлагаю вспомнить о metanit. На мой +взгляд, тема Stream API дана там поверхностно, но хорошо подойдет для первого знакомства. + +На данном этапе предлагаю обратить внимание на следующие статьи (рекомендую смотреть после ознакомления с теоретической +частью данной статьи): + +- [Первая](https://metanit.com/java/tutorial/10.1.php) +- [Вторая](https://metanit.com/java/tutorial/10.2.php) +- [Третья](https://metanit.com/java/tutorial/10.3.php) +- [Четвертая](https://metanit.com/java/tutorial/10.11.php) + +Также не советую пытаться запомнить все методы `Stream` и связанных с ним классов. Их тяжело зубрить, зато они легко +запоминаются при наличии практики и понимании классификации. В следующих уроках мы ознакомимся как с методами Stream'ов, +так и с их классификацией. Она шире, чем у `Optional` и очень важна для эффективного применения. В целом, Stream'ы +обещают стать крупным разделом в текущем курсе. + +Пока предлагаю остановить рассмотрение самого Stream API и разобраться с понятиями и принципами, которые за ним стоят. +Впрочем, эти же принципы можно считать основополагающими для всего ФП в Java. + +## Функции высшего порядка + +Думаю, вы обратили внимание на несколько непривычный способ обработки данных с помощью `Optional` и `Stream`. +Для наглядности, возьмем пример из [урока 48](https://telegra.ph/Funkcionalnoe-programmirovanie-v-Java-02-14): + +```java + return company.getDepartments() + .stream() + .map(Department::getEmployees) + .flatMap(Collection::stream) + .filter(Objects::nonNull) + .collect(Collectors.groupingBy(Employee::getAge)); +``` + +Разберемся подробнее с некоторыми строками в этом примере: + +`company.getDepartments()` – здесь все понятно. Вызываем метод (геттер) у переменной типа `Company`, получаем список +объектов типа `Department`; + +`stream()` – в новинку, но тоже несложно: делаем stream из списка отделов. На самом деле, сложно. Но об этом не сегодня. +Пока предлагаю относиться к способу создания Stream'а из коллекции как к черному ящику; + +А вот дальше начинается нечто уже знакомое, но непривычное: + +`map(Department::getEmployees)` – мы вызываем функцию (метод), которая параметром принимает другую функцию. Да, на самом +деле параметром передается объект анонимного класса, но сути это не отменяет: мы передаем в одну функцию другую. + +Опустим оставшуюся часть цепочки – она, в данном разрезе, однотипна с указанным выше `map()`. + +Метод `map()` принимает параметром объект типа `Function`. `Function` является одним из популярных функциональных +интерфейсов. Но важно не это. Объект типа `Function` – это такой же Java-объект, как и объект классов `Object`, `String` +или, например, `Animal` из практических заданий нашего курса. `Animal`, к слову тоже можно представить как +функциональных интерфейс в некоторых из уроков. + +Так вот. Если объект `Function` – это обычный Java-объект, то его можно возвращать из методов. Т.е. мы можем написать +следующий метод: + +```java + Function> getEmployees() { + return Department::getEmployees; + } +``` + +И тогда + +``` + map(Department::getEmployees) +``` + +можно будет представить как + +``` + map(getEmployees()) +``` + +> !NB: не советую усердствовать с подобными обертками для лямбда выражений – чаще они сделают код менее понятным, в +> сравнении с явным описанием лямбда выражения/method reference’а с использованием характерных +> операторов `«->»` или `«::»`. Но и оформление выше синтаксически возможно и иногда используется. + +В случае с `map()` мы принимали лямбда-выражение параметром, в случае с самописным `getEmployees()` – возвращали +лямбда-выражение в качестве результата метода. В обоих случаях мы, по сути, работали с функцией (лямбда-выражением) как +с объектом. + +Достаточно важно понимать как «физическую» часть этого механизма – работу с объектом анонимного класса, описанного +лямбдой, так и абстракцию – работу с функцией (ведь лямбда-выражение в итоге и будет использована как функция для +обработки каких-то данных). + +Физическую часть мы постарались разобрать в уроках о +[лямбда-выражениях](https://telegra.ph/Funkcionalnye-interfejsy-i-lyambda-vyrazheniya-02-10) +и [method reference’ах](https://telegra.ph/Ssylka-na-metod-02-12). +А для описания абстракции познакомимся с термином **функция высшего порядка**. + +**Функция высшего порядка** – функция (в констексте Java – метод), принимающая в качестве параметра другие функции и/или +возвращающая другую функцию в качестве результата. + +Функциональное программирование в большинстве языков так или иначе использует цепочки вызовов, в которых функции высшего +порядка (`map()` – одна из самых распространенных, безотносительно ЯП) принимают в качестве параметров другие функции. + +Таким образом известные нам методы `Optional` `map()`, `flatMap()`, `filter()` – функции высшего порядка. + +Вряд ли информация из этого (и следующего) пунктов статьи помогут вам писать код лучше прямо сейчас, но они позволят +более системно подойти к ФП в Java. + +## Паттерны проектирования или почему Монада – это не страшно + +Чем больше мы будем работать с Java, тем меньше мы будем говорить о синтаксисе и больше – о конкретных инструментах, а +также принципах и подходах, которые лежат в основе этих инструментов. + +Для того, чтобы подобную информацию легче воспринимать, придется постепенно знакомиться с некоторыми специфичными +терминами. В рамках данного пункта рассмотрим понятие паттерна проектирования. + +**Паттерн (шаблон) проектирования** – он же **design pattern** – общая схема решения часто возникающей проблемы +проектирования. + +Другое определение – типовое решение распространенной архитектурной проблемы. + +Для понимания, что же такое паттерн проектирования, стоит уточнить две важные детали: + +1. В отличии от алгоритма, паттерн проектирования почти никогда не получится описать в виде кода/псевдокода. Поскольку + паттерны, как правило, описывают отношения различных сущностей (классов), каждая реализация уникальна и зависит от + особенностей каждой конкретной системы или задачи. Кроме того, один и тот же паттерн можно реализовать различными + средствами даже в рамках одной задачи. Проще говоря, если алгоритм является четким описанием последовательности + действий, результатом которого является решение проблемы, то паттерн – верхнеуровневое описание такого решения. +2. Проблема решаемая паттерном обычно более глобальна, чем проблема, решаемая алгоритмом. И паттерн не дает решения + какой-то проблемы в чистом виде. Он является именно схемой (чаще всего - **UML-диаграммой**), реализация которой + позволяет проблему решить. + +> Как правило, основная проблема не в реализации паттерна, а в том, чтобы понять, что проблема существует и имеет +> органичное решение, которое паттерн и описывает. + +Для примера рассмотрим примитивный паттерн проектирования. Строго говоря, его (и ему подобных) не считают паттерном (я +нашел термин **«идиома программирования»**, но не слышал, чтобы его использовали), но он поможет понять, что +подразумевается под шаблонами проектирования. + +**Бесконечный цикл**. Полагаю, почти каждый с ним так или иначе сталкивался. Он может быть реализован по-разному: с +помощью `for` или `while`, например. Или использовать (не использовать) побочный способ выхода из цикла - `break` +или `return`. Но на суть это не влияет. + +Пусть будет: + +```java + while (true) { + //do sth + + if (/*some condition*/) { + break; + } + } +``` + +Бесконечный цикл не регламентирует ничего, кроме самого факта: условие завершения цикла заведомо невыполнимо. +В случае с `while`, цикл завершается, когда условие в скобках становится `false`. Но `true != false` всегда:) + +Такой цикл, например, является решением для следующих задач: + +- Бесконечное оповещение. Оповещение трейдера об изменении котировок выбранной акции на бирже, изменение значения + времени на часах, явный вызов garbage collector’а с интервалом в секунду (не знаю зачем, но вдруг) – все это разные + задачи, но всех их можно свести к непрерывному вызову какого-то метода. И все эти задачи решает бесконечный цикл. + Условие его завершения может отсутствовать (будет работать до завершения программы) или быть указанным (действие + пользователя, например); + +> В одной из задач последних уроков было необходимо сделать интерактивное меню для взаимодействия с пользователем. +> Классическая реализация такого меню в консольном приложении строится на бесконечном цикле. + +- Ожидание чего-либо. Доступа к ресурсу, занятому другим потоком, или проверка наличия пользовательского ввода – не + важно. В любом случае задача решается бесконечным циклом, который на каждой итерации проверяет, появился ли (доступен + ли) предмет ожидания. И пока не появится – будет проверять вновь и вновь. Главное не забывать, что + _«паттерн решает задачу» != «паттерн решает задачу эффективно»_ + +Полагаю, термин "паттерн проектирования" более-менее понятен. + +Паттернов проектирования много и бывают они разными. Мы их будем изучать в дальнейшем, как опосредовано, так и в рамках +специально отведенных уроков. Пока отметим, что паттерны проектирования, зачастую, зависят от парадигмы +программирования. Не все, но многие. + +И сегодня мы познакомимся с одним из паттернов функционального программирования: **монадой**. + +**Монада** – паттерн проектирования, описывающий контейнер обобщенного типа, а также набор методов, позволяющие +производить действия над хранимым значением, результатом которых будет новый (или тот же, не суть) объект контейнера. + +В Java-реализации применение монады выглядит как цепочка вызовов функций высшего порядка над объектом(-ами), хранящимся +в параметризованном поле. Именно реализацией монады, в конечном итоге, являются `Optional`, `Stream` (с некоторыми +особенностями) и некоторые другие инструменты как поставляемые Java по умолчанию, так и реализованные в популярных +внешних библиотеках и фреймворках. + +Реализации будут решать разные проблемы, могут иметь различную функциональность и предназначаться для совершенно +непохожих между собой прикладных задач. Но понимая, что все это реализации одного и того же паттерна, гораздо проще +овладевать новыми реализациями - общий смысл и способ использования заранее понятен, хотя бы на уровне основных +синтаксических конструкций. + +#### С теорией на сегодня все! + +![img.png](../../../commonmedia/defaultFooter.jpg) + +Переходим к практике. + +Сегодня попробуем построить практическую часть по-новому: воспользуемся задачами на сторонних сайтах. + +Представляю вашему вниманию несколько ссылок. К сожалению, описание заданий дано на английском, но они понятны даже с +помощью встроенного переводчика. + +## Задача 1: + +Несколько ссылок в рамках одного курса. Затрагивает и `Stream`, и `Optional`. Из плюсов — можно сразу проверить, решает +ли ваш код поставленную задачу. Также каждая страница содержит ссылку на примеры решений (организованно не очень удобно, +но решения найти можно). + +Прикладываю только ссылки с практикой, с помощью навигации можете посмотреть и страницы с теорией. + +### Stream API + +- [Первая](https://www.codingame.com/playgrounds/20782/java-guild-meeting-52018/streams---intro) +- [Вторая](https://www.codingame.com/playgrounds/20782/java-guild-meeting-52018/streams---practice) +- [Третья](https://www.codingame.com/playgrounds/20782/java-guild-meeting-52018/streams---micro-katas) + +### Optional + +- [Ссылка](https://www.codingame.com/playgrounds/20782/java-guild-meeting-52018/optionals---practice) + +## Задача 2: + +И еще одна +[подборка задач](https://blog.devgenius.io/15-practical-exercises-help-you-master-java-stream-api-3f9c86b1cf82). +Плюс ее в том, что задачи разнообразные и идут с усложнением — подозреваю, часть из них будет +тяжело решить сейчас, к ним стоит возвращаться по мере изучения Stream API в рамках следующих уроков. + +Минусы — подборка рассчитана на более опытную аудиторию. Предлагаю каждую из задач воспринимать как необходимость +реализовать метод, параметром которого приходит список значений нужного типа. Еще одним минусом можно считать то, что +решение демонстрируется сразу после описания задачи. Надеюсь, вам хватит силы воли, чтобы избежать подглядывания. + +> Если что-то непонятно или не получается – welcome в комменты к посту или в лс:) +> +> Канал: https://t.me/ViamSupervadetVadens +> +> Мой тг: https://t.me/ironicMotherfucker +> +> **Дорогу осилит идущий!** \ No newline at end of file