Про книгу «Современный язык Java»

Обложка "Современный язык Java"В последнее время помимо языка Python (которому, кстати, на днях исполнилось 30 лет) я активно стал использовать Java, поэтому решил углубить свои знания в этом языке и почитать более специализированные книги, которые поподробнее рассказывают об отдельных элементах языка или библиотек. Этот пост будет посвящен одной из таких книг, которую в целом можно охарактеризовать словами «как нам из Java сделать функциональный язык программирования». Речь пойдет о книге трех авторов Рауля-Габриэля Урма, Марио Фуско и Алана Майкрофта «Современный язык Java», в оригинале она называется «Modern Java in Action». Англоязычная версия книги была издана в 2018 году, и на тот момент были актуальные версии Java 8 и 9, а Java 10, который только-только вышел.

Большая часть книги основана на использовании лямбда-выражений (они же анонимные функции), о которых очень подробно рассказывается в первых главах книги. На мой взгляд даже чересчур подробно, так что возникает ощущение, что авторы повторяют одно и то же чуть под разными углами зрения. В Java 8 помимо анонимных функций появилась возможность создавать ссылки на методы классов, о том, как это работает, и как ссылки на методы связаны с лямбда-выражениями и функциональными интерфейсами (интерфейсами, у которых есть только один абстрактный метод) и рассказывается в первых главах на множестве небольших примеров. А для самых любопытных и продвинутых в приложении даже приводится анализ байт-кода, который создается компилятором для создания анонимных функций.

Достаточно много внимания отводится потокам данных (Stream API), которые позволяют строить конвейер обработки данных, избавляя код от громоздких последовательных или вложенных циклов. Для использования потоков данных как раз активно применяются лямбда-выражения и ссылки на методы, о которых рассказывалось до этого. Описание Stream API очень подробное, но это не просто пересказ документации, а последовательное хорошо структурированное изложение сначала идеи потоков данных, потом рассказывается о том, как фильтровать данные в потоке и собирать элементы потока в коллекции. Рассказывается, какие есть методы для усечения потока данных (например, если из всего потока нужно взять только каждый n-ый элемент или после определенного условия прекратить обрабатывать поток, даже если в нем есть другие элементы).

В этой же части книги рассказывается и о преобразовании элементов потока в другие объекты (mapping), а так же о методах свертки — например, если нужно посчитать сумму или произведение элементов, какие-то более сложные операции, а потом вернуть не коллекцию элементов, а один элемент или число. Мне понравилось, что в книге описаны не только стандартные классы, реализующие интерфейсы работы с потоками данных, но и то, как эти интерфейсы можно реализовать самостоятельно для каких-то более экзотических задач.

На мой взгляд авторы иногда слишком увлекаются функциональным стилем программирования, что не всегда идет на пользу наглядности кода. Все-таки многократно вложенные друг в друга лямбда-выражения, даже несмотря на сокращенную форму, которая появилась в Java 8, когда код начинает напоминать Lisp с многократно вложенными друг в друга круглыми скобками, тяжело читать. Наверное, в реальности все эти вложенные друг в друга лямбда-выражения стоило бы оформить в виде отдельных методов, и тогда код стал бы намного нагляднее. К сожалению, в бумажной книге нет такой замечательной возможности из редактора кода, когда можно подсвечивать парные скобки, и поэтому тяжело понять, где проходят границы между разными лямбда-выражениями и методами интерфейса. Кстати, про читаемость кода в книге есть отдельная глава, где авторы тоже пишут, что иногда лямбда-выражения не стоит писать «по месту», а желательно вносить их в отдельные методы. В этой же главе приводятся примеры, как с помощью лямбда-выражений и указателей на методы можно реализовать различные паттерны проектирования из книги Банды четырех. Приводятся примеры шаблонов Стратегия, Шаблонный метод, Наблюдатель, Цепочка обязанностей и Фабрика.

В книге много говорится о параллельной обработке потоков данных: когда такая обработка возможно, как ее запустить, как Java делит поток данных на блоки, чтобы они обрабатывались параллельно, как на этот процесс может влиять пользователь и т.д. Рассказывается, как использовать классы RecursiveTask и RecursiveAction, рассматривается интерфейс Spliterator, который предназначен для указания того, как поток данных может разбиваться на блоки.

Отдельная глава посвящена новым возможностям Collection API, появившимся в Java 8 и Java 9. В основном речь идет о фабриках коллекций, которые позволяют создавать коллекции и заполнять их данными одним выражением вместо того, чтобы сначала создавать коллекцию, а потом добавлять в нее по одному элементу.

Одна глава посвящена классу Optional, который по задумке должен быть использован в тех местах, где обычно используют null, чтобы показать, что какое-то значение отсутствует. Как известно, NullPointerException — чуть ли не самое частое неожиданное исключение, которое возникает в программах на Java, когда где-то забыли проверить параметр или значение на равенство null. Если вместо null использовать класс Optional, который может содержать или не содержать значение, то такой проверки избежать не удастся (компилятор не позволит), если только вы намеренно не будете вызывать специальный метод класса, предполагающий, что класс Optional в данный момент обязательно содержит значение. В свое время я очень проникся этой идеей при программировании на языке Rust — там значения null нет в принципе, и все подобные проблемы, когда данных может не существовать, решаются с помощью Optional. Но использование Optional требует некоторой дисциплины, потому что очень уж прочно вошло в привычку элементам класса без значений присваивать null (и компилятор этому способствует).

Еще одна глава посвящена новым классам для работы с датами и временем: LocalDate, LocalTime, LocalDateTime, Instant, Duration и Period. Тут особо рассказывать нечего. Просто более удобные классы, которые пришли на замену Date, Calendar и т.п.

Единственная глава, в которой, на мой взгляд, тема раскрыта не настолько подробно, как хотелось бы — это глава про систему модулей. Модули появились в Java 9, и когда ими начинаешь пользоваться, то на первый взгляд кажется, что там все очень просто — кладешь в папку пакета файл module-info.java и перечисляешь в нем, что нужно импортировать, а что экспортировать. Но когда я в первый раз столкнулся с тем, что нужно обернуть свой пакет в модуль, стали возникать не совсем очевидные вопросы, например, как теперь организовывать юнит-тесты? Про модули пишут целые книги, например, недавно на русском языке вышла книга Н. Парлога «Система модулей Java», на которую несколько раз ссылались авторы книги «Современный язык Java», признавая, что в их книге модули рассматриваются поверхностно (и это действительно так), а более подробно читайте в книге Парлога.

Еще пара глав посвящены таким интересным темам как асинхронное и реактивное программирование. Если говорить, про асинхронщину, то рассказывается о том, как писать код с использованием интерфейса Future, а затем какие возможности предоставляет класс CompletableFuture, который тоже реализует интерфейс Future.

В главе про реактивное программирование сначала подробно описывается, что это такое, какие требования предъявляются к приложению, чтобы они могли называться реактивными (манифест реактивности), рассказывается о проблемах, которые могут возникать в случае архитектуры подписка — подписчик (когда события в подписке генерируются быстрее, чем их может обрабатывать подписчик), и как эти проблемы обычно решают. В этой главе говорится о Flow API и интерфейсах, которые входят в этот API, а также о том, почему в стандартной библиотеке Java нет классов, которые эти интерфейсы реализуют. В качестве примера библиотеки, который следует интерфейсам Flow API используется RxJava, но есть пример кода, в котором не используются сторонние библиотеки, и показано, как можно самостоятельно реализовать интерфейсы из Flow API.

Последняя часть книги посвящена тому, как из Java сделать функциональный язык программирования. Честно говоря, я не сторонник того, чтобы натягивать сову на что-нибудь круглое, и считаю, что программировать нужно с использованием тех парадигм, на которые язык рассчитан, а если предлагаемая парадигма не устраивает, то лучше перейти на другой язык. Но если уж авторы языка Java начали добавлять в Java элементы функционального языка, то почему бы ими не воспользоваться. На самом деле последняя часть книги — это больше не про Java, а функциональное программирование в целом, и даже отдельная глава посвящена языку Scala и сравнению кода, написанного на Scala и Java (сравнение не в пользу Java), описываются принципиальные ограничения виртуальной машины Java, которые не позволяют языку Scala быть более быстрым (нет оптимизации хвостовой рекурсии). Но почитать главы про функциональщину для общего развития стоит — они получились интересными.

А в завершении книги в приложениях описываются другие более мелкие новшества, которые появились в Java 8-10, некоторые из них достаточно низкоуровневые.

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

Удивил способ оформления комментариев в этой книге:

Оформление комментариев

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

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

В целом я бы поставил книге оценку 4 из 5. Не шедевр, но вполне полезное руководство по относительно новым возможностям Java.

PS. Вы можете подписаться на новости сайта через RSS, Группу Вконтакте или Канал в Telegram.

Пожалуйста, оцените запись

УжасноПлохоТак себеХорошоОтлично (Количество голосов: 3, средняя оценка: 2,33)
Загрузка...

Leave a comment

Subscribe without commenting