[Программирование, Java] Java 15 и IntelliJ IDEA (перевод)

Автор Сообщение
news_bot ®

Стаж: 6 лет 9 месяцев
Сообщений: 27286

Создавать темы news_bot ® написал(а)
07-Июн-2021 20:31

В Java 15 появились sealed-классы и sealed-интерфейсы, с помощью которых стало возможным ограничивать иерархию классов и интерфейсов на уровне синтаксиса языка. Теперь возможные иерархии определяются декларативно. Этот функционал пока представлен в режиме превью (preview). Также в Java 15 есть изменения в записях (Records), появившихся в Java 14. А сопоставление с образцом (pattern matching) для instanceof вошло в Java 15 как второе превью без изменений. Текстовые блоки (text block) из Java 13 включены в Java 15 как стандартная языковая конструкция. Изменений в них по сравнению с Java 14 нет. 
В этой статье я расскажу обо всех новых и обновленных языковых конструкциях Java 15, о том, как они вам могут пригодиться, и как их использовать в IntelliJ IDEA. Давайте начнем. Sealed-классы и интерфейсыОпределяя класс как sealed, вы можете явно указать, каким классам разрешено его расширять. Это, с одной стороны, позволяет использовать класс повторно через наследование, а с другой —  ограничить допустимых наследников. Но зачем вам ограничивать иерархии наследования?Необходимость ограниченных иерархийПредставьте, что вы разрабатываете приложение для садовников. Садовнику, в зависимости от вида растения, требуется выполнять различные действия. Давайте смоделируем иерархию растений следующим образом (я намеренно не привожу полный текст классов): 
class Plant {}
class Herb extends Plant {}
class Shrub extends Plant {}
class Climber extends Plant{}
class Cucumber extends Climber {}
Ниже приведен пример того, как класс Gardener (садовник) может использовать эту иерархию классов: 
public class Gardener {
   int process(Plant plant) {
       if (plant instanceof Cucumber) {
           return harvestCucumber();
       } else if (plant instanceof Climber) {
           return sowClimber();
       } else if (plant instanceof Herb) {
           return sellHerb();
       } else if (plant instanceof Shrub) {
           return pruneShrub();
       } else {
           System.out.println("Unreachable CODE. Unknown Plant type");
           return 0;
       }
   }
   private int pruneShrub() { .. }
   private int sellHerb() { .. }
   private int sowClimber() { .. }
   private int harvestCucumber() { .. }
}
Проблема здесь в том, что разработчику необходимо предусмотреть ветку else для контроля ситуации, когда другой разработчик добавит класс в эту иерархию. Sealed-классы помогают наложить необходимые ограничения на уровне языка. Определение защищенных иерархий с помощью sealed-классовОбъявить sealed-класс можно с помощью модификатора sealed. Для указания классов, которые могут его расширять напрямую, используется ключевое слово permits. Подклассы могут быть final, non-sealed или sealed.Gif'ка ниже показывает, как изменить объявление обычного класса на sealed-класс и модифицировать его наследников:
Вот измененный код:
sealed public class Plant permits Herb, Shrub, Climber {
}
final class Herb extends Plant {}
non-sealed class Shrub extends Plant {}
sealed class Climber extends Plant permits Cucumber{}
final class Cucumber extends Climber {}
Позволяя расширять класс только определенному перечню классов, вы можете отделить доступность (accessibility) от расширяемости (extensibility). Можно сделать sealed-класс доступным для других пакетов и модулей и контролировать, кто может его расширять. В прошлом, чтобы предотвратить расширение классов, разработчики создавали package-private классы. Однако это также ограничивало к ним доступ. Для sealed-классов это уже не так.Перечень permitted-подклассов доступен через рефлексию (reflection) с помощью метода Class.permittedSubclasses(). Можно получить всю sealed-иерархию в рантайме.Давайте быстро проверим вашу конфигурацию IntelliJ IDEA, чтобы убедиться, что вы сможете запустить код примеров.Конфигурация IntelliJ IDEAВозможности Java 15 поддерживаются в IntelliJ IDEA с версии 2020.2, выпущенной в июле 2020 года. Для настройки использования Java 15 выберите в свойствах проекта и модулей в параметре "Project SDK" значение "15", а в "Project language level" — "15 (Preview) – Sealed types, records, patterns, local enums and interfaces".
Также вы можете скачать Java 15 непосредственно из IntelliJ IDEA. Для этого в левой части окна "Project Structure" в разделе "Platform Settings" выберите "SDKs", затем нажмите вверху значок "+" и выберите "Download JDK". Укажите поставщика (Vendor), версию (Version) и каталог для загрузки JDK.Возвращаемся к обработке подтипов Plant в классе GardenerПри создании sealed-иерархии вы знаете полный список наследников и вам не нужно обрабатывать какие-то общие случаи. Ветка else в методе process() класса Gardener никогда не будет выполнена. Однако нам все-равно нужно оставить else из-за return.Сопоставление с образцом, добавленное в Java 14 для instanceof, может появиться в будущих версиях Java и в выражениях switch. С помощью улучшенного switch можно работать с полным списком наследников. Это позволит исключить написание любого "обобщенного кода" для обработки ситуаций, когда передается непредусмотренный подтип Plant:
// Этот код не работает в Java 15.
// Он будет работать в будущих версиях Java
// после реализации type-test-pattern в switch
int processInAFutureJavaVersion(Plant plant) {
   return switch (plant) {
       case Cucumber c -> c.harvestCucumber();
       case Climber cl -> cl.sowClimber();
       case Herb h -> h.sellHerb();
       case Shrub s -> s.pruneShrub();
   }
}
Пакеты и модулиSealed-классы и их реализации должны находиться в одном модуле. Если базовый  sealed-класс определен в именованном модуле, то все его реализации должны быть определены там же. Но они могут быть в разных пакетах.Для sealed-класса, определенного в безымянном модуле, все его реализации должны быть в одном пакете.Правила для базовых классов и наследников классовКлассы, расширяющие sealed-класс, должны быть объявлены как final, non-sealed или sealed. Модификатор final запрещает дальнейшее расширение, non-sealed позволяет другим классам расширять его, а sealed-подкласс должен следовать тем же правилам, что и родительский базовый класс — необходимо явно указать список классов, которые могут его расширять.Sealed-класс также может быть абстрактным. Его наследники могут быть как абстрактными, так и конкретными классами.Давайте изменим набор классов, используемый в предыдущем разделе, и определим класс Plant как абстрактный с абстрактным методом grow(). Поскольку производный класс Herb является final-классом, то в нем должна быть реализация метода grow(). Non-sealed класс Shrub объявлен абстрактным и может не реализовывать метод grow(). Sealed-класс Climber реализует абстрактный метод grow():
Вот измененный код:
sealed abstract public class Plant permits Herb, Shrub, Climber {
   abstract void grow();
}
final class Herb extends Plant {
   @Override
   void grow() {
   }
}
non-sealed abstract class Shrub extends Plant {}
sealed class Climber extends Plant permits Cucumber{
   @Override
   void grow() {
   }
}
final class Cucumber extends Climber {}
Если вы определяете sealed-класс и его наследников в одном файле исходного кода, то можно опустить модификатор permits и имена подклассов, указанных в объявлении sealed-класса. В этом случае компилятор способен самостоятельно вывести иерархию.Sealed-интерфейсыSealed-интерфейс позволяет явно указать интерфейсы, которые могут его расширять, и классы (включая записи), которые могут его реализовать. Для интерфейсов применяются правила, аналогичные sealed-классам.Однако поскольку вы не можете объявить интерфейс с помощью модификатора final (иначе это противоречило бы его назначению, так как интерфейсы должны быть реализованы) интерфейс может быть объявлен только с использованием модификаторов sealed или non-sealed. В разделе permits перечисляются классы, которые непосредственно могут реализовать sealed-интерфейс, и интерфейсы, которые могут его расширять. Реализующий класс может быть final, sealed или non-sealed. Поскольку записи, появившиеся в Java 14, неявно являются final, то они не нуждаются в каких-либо дополнительных модификаторах:
sealed public interface Move permits Athlete, Person, Jump, Kick {
}
final class Athlete implements Move {}
record Person(String name, int age) implements Move {}
non-sealed interface Jump extends Move {}
sealed interface Kick extends Move permits Karate {}
final class Karate implements Kick {}
Давайте перейдем к следующему нововведению Java 15 – локальные записи (record).Записи (records)Записи (records) предназначены для компактной записи объектов-значений (value object). Первое превью записей появилось в Java 14, а в Java 15 — второе превью с некоторыми изменениями.Если вы не знакомы с записями или хотите узнать об их поддержке в IntelliJ IDEA, то  обратитесь к статье Java 14 и IntelliJ IDEA. В IntelliJ IDEA есть множество функций, которые помогут вам создавать и использовать записи. В этом посте я расскажу об изменениях в Java 15 по сравнению с Java 14.Java 15 позволяет определять локальные записи внутри метода. В следующем примере метод getTopPerformingStocks() ищет акции (stock), которые имеют наибольшую стоимость на указанную дату, и возвращает их названия.
List<String> getTopPerformingStocks(List<Stock> allStocks, LocalDate date) {
   // TopStock - локальная запись (Record)
   record TopStock(Stock stock, double stockValue) {}
   return allStocks.stream()
              .map(s -> new TopStock(s, getStockValue(s, date)))
              .sorted((s1, s2) -> Double.compare(s1.stockValue(), s2.stockValue()))
              .limit(2)
              .map(s -> s.stock.getName())
              .collect(Collectors.toList());
}
Локальные интерфейсы и перечисленияJava 15 также позволяет объявлять локальные перечисления и интерфейсы. Внутри метода можно инкапсулировать локальные для него данные или бизнес-логику.
public void createLocalInterface() {
   interface LocalInterface {
       void aMethod();
   }
   // Код, использующий LocalInterface
}
public void createLocalEnum() {
   enum Color {RED, YELLOW, BLUE}
   // Код, использующий enum Color
}
Однако в этих случаях нельзя использовать контекстные переменные. Например, для создания значений перечисления FOO и BAR нельзя использовать параметры метода:
void test(int input) {
   enum Data {
       FOO(input), BAR(input*2); // Ошибка. Нельзя обращаться к input
       private final int i;
       Data(int i) {
           this.i = i;
       }
   }
}
Сопоставление с образцом (pattern matching) для instanceofМногие Java-разработчики используют оператор instanceof для сравнения типов. Если результат сравнения будет true, то далее следует явное приведение к типу, с которым сравнивали. Этот паттерн применяется довольно часто и выглядит следующим образом: сравнение - ifTrue - приведениеКТипу.В Java 14 оператор instanceof стал проще за счет поддержки в нем сопоставления с образцом. Дополнительные переменные и явное приведение больше не нужны, что делает ваш код безопаснее и лаконичнее.Это уже второе превью сопоставления с образцом для instanceof (изменений по сравнению с Java 14 нет).Подробнее узнать о поддержке этой функциональности в IntelliJ IDEA вы можете в статье Java 14 и IntelliJ IDEA. Там также есть несколько интересных примеров того рефакторинга кода с помощью этой возможности и других инспекций из IntelliJ IDEA, таких как объединение вложенных if и извлечение или инлайнинг переменных.Текстовые блокиМногострочные строки и текстовые блоки были добавлены в Java 15 как стандартная языковая конструкция без каких-либо изменений по сравнению с Java 14.Подробнее о поддержке текстовых блоков в IntelliJ IDEA вы можете узнать в статье Java 14 и IntelliJ IDEA.ПревьюSealed-классы и интерфейсы появились в Java 15 в качестве превью. С новым релизным циклом в шесть месяцев новые языковые конструкции выпускаются в режиме превью. И в дальнейшем они могут появиться повторно в более поздних версиях в качестве второго или третьего превью с изменениями или без них. Как только они станут достаточно стабильными, они могут быть добавлены в стандарт языка.Превью версии являются полноценными, но могут измениться, что, по сути, означает готовность функциональности к использованию разработчиками, но ее детали могут поменяться в будущих релизах Java в зависимости от отзывов. В отличие от API, языковые конструкции не могут в будущем быть объявлены устаревшими (deprecated). Если вы хотите высказать свое мнение о каких-либо превью возможностях, то можете сделать это в списке рассылки JDK (требуется бесплатная регистрация).По указанной выше причине IntelliJ IDEA поддерживает превью возможности Java только для текущего JDK. Реализация превью возможностей может измениться от версии к версии, пока они не будут удалены или добавлены в качестве стандарта. Код, использующий превью возможности из более ранней версии Java SE, может не компилироваться или не запускаться в новой версии. Например, Switch Expressions в Java 12 использовали break для возврата значения из ветки, а позже это было изменено на yield. Поддержка использования break для возврата значения из Switch Expressions уже отсутствует в IntelliJ IDEA.РезюмеIntelliJ IDEA стремится не только к поддержке новых функций Java, но и к разработке для них новых проверок и инспекций.IntelliJ IDEA 2020.2 поддерживает все новые языковые конструкции Java 15. Попробуйте уже сегодня sealed-классы и интерфейсы, а также записи (record), сопоставление с образцом (pattern matching) для instanceof и текстовые блоки (text block). Скачать IntelliJ IDEA вы можете по этой ссылке.Мы всегда рады обратной связи от наших пользователей. Не забудьте оставить отзыв о поддержке этих возможностей в IntelliJ IDEA.А прямо сейчас в OTUS открыт набор на новый поток курса Java Developer. Professional. Приглашаю всех желающих на demo day курса, в рамках которого можно будет подробно ознакомиться с программой и процессом обучения, а также задать вопросы экспертам OTUS.
===========
Источник:
habr.com
===========

===========
Автор оригинала: Mala Gupta
===========
Похожие новости: Теги для поиска: #_programmirovanie (Программирование), #_java, #_intellij_idea, #_java, #_blog_kompanii_otus (
Блог компании OTUS
)
, #_programmirovanie (
Программирование
)
, #_java
Профиль  ЛС 
Показать сообщения:     

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

Текущее время: 22-Ноя 13:18
Часовой пояс: UTC + 5