Всем привет! Современный Java (давайте возьмем за точку отсчета…
Всем привет! Современный Java (давайте возьмем за точку отсчета свежий LTS — JDK 21) стал невероятно прагматичным. Нам дали кучу полезных инструментов, позволяющих языку сохранять лидирующие позиции, конечно мы не берем тут тренд ИИ, но и тут я бы поспорил.
Давайте разберем главные фичи, из которых теперь собирается любой адекватный бэкенд.
1. Records
Сколько раз вы писали классический DTO или сущность-значимость? Пять полей, конструктор, геттеры, `equals()`, `hashCode()`, `toString()` — полсотни строк ради простой передачи данных. С `record` это умещается в одну:
* Под капотом: Это неизменяемый (`final`) класс. Поля приватны, публичные только геттеры (с именами самих полей), а методы `equals/hashCode/toString` генерируются идеально корректно. Никакой магии библиотек вроде Lombok — это часть спецификации языка.
* Где использовать: DTO, сущности-значимости, возвращаемые значения из сервисов.
* Чего избегать: Не делайте из рекорда полноценную Entity со спящим состоянием lazy-loading и связями JPA. Рекорд должен быть плоским куском данных.
2. Закрытые иерархии: Sealed Classes
Помните времена, когда кто угодно мог наследоваться от вашего абстрактного Animal? Теперь мы можем закрыть дверь для чужаков:
Модификатор `non-sealed`. позволяет точечно разрешить расширение ветки, если бизнес-логика того требует.
* Зачем это нужно: Компилятор точно знает всех наследников. Это открывает невероятные возможности для Pattern Matching. Если у нас есть статус заказа `Pending`, `Paid`, `Shipped`, никто случайно не создаст `MagicStatusFromAnotherDepartment`.
* Важно: Все классы-пермиты должны лежать в одном модуле (а лучше — в одном пакете) - это правило инкапсуляции.
3. Деконструкция объектов: Pattern Matching for switch
Связка двух предыдущих пунктов дает нам главный инструмент современного Java. Больше никаких `instanceof`:
А благодаря `sealed` интерфейсам компилятор сам проверит, все ли варианты мы покрыли. Если убрать ветку `default` и не учесть какой-то из разрешенных пермитов — IDE подсветит ошибку еще до коммита.
Кстати, паттерн-матчинг работает не только в `switch`.
4. Switch перестал быть просто оператором перехода. Во-первых, он стал выражением (возвращает значение, пример выше). Во-вторых, появилась стрелка:
5. Многострочные строки: Text Blocks
SQL-запросы, JSON, HTML прямо в коде без лесенки из кавычек и плюсов конкатенации:
Три двойные кавычки сохраняют форматирование. Метод `.formatted()` (пришедший на смену `String.format()`) делает вставку переменных чистой и безопасной. Идеально для написания интеграционных тестов, где мокируемый ответ API пишется прямо над тестом.
Давайте разберем главные фичи, из которых теперь собирается любой адекватный бэкенд.
1. Records
Сколько раз вы писали классический DTO или сущность-значимость? Пять полей, конструктор, геттеры, `equals()`, `hashCode()`, `toString()` — полсотни строк ради простой передачи данных. С `record` это умещается в одну:
public record User(UUID id, String email, boolean active) {}
* Под капотом: Это неизменяемый (`final`) класс. Поля приватны, публичные только геттеры (с именами самих полей), а методы `equals/hashCode/toString` генерируются идеально корректно. Никакой магии библиотек вроде Lombok — это часть спецификации языка.
* Где использовать: DTO, сущности-значимости, возвращаемые значения из сервисов.
* Чего избегать: Не делайте из рекорда полноценную Entity со спящим состоянием lazy-loading и связями JPA. Рекорд должен быть плоским куском данных.
2. Закрытые иерархии: Sealed Classes
Помните времена, когда кто угодно мог наследоваться от вашего абстрактного Animal? Теперь мы можем закрыть дверь для чужаков:
public sealed interface Shape permits Circle, Square {
double area();
}
// В другом файле
public final class Circle implements Shape { ... }
public non-sealed class WeirdShape implements Shape { /* может наследоваться дальше */ }
Модификатор `non-sealed`. позволяет точечно разрешить расширение ветки, если бизнес-логика того требует.
* Зачем это нужно: Компилятор точно знает всех наследников. Это открывает невероятные возможности для Pattern Matching. Если у нас есть статус заказа `Pending`, `Paid`, `Shipped`, никто случайно не создаст `MagicStatusFromAnotherDepartment`.
* Важно: Все классы-пермиты должны лежать в одном модуле (а лучше — в одном пакете) - это правило инкапсуляции.
3. Деконструкция объектов: Pattern Matching for switch
Связка двух предыдущих пунктов дает нам главный инструмент современного Java. Больше никаких `instanceof`:
double area = switch (shape) {
case Circle c -> Math.PI * c.radius() * c.radius();
case Square s -> s.side() * s.side();
default -> throw new IllegalStateException("Неизвестная фигура!");
};
А благодаря `sealed` интерфейсам компилятор сам проверит, все ли варианты мы покрыли. Если убрать ветку `default` и не учесть какой-то из разрешенных пермитов — IDE подсветит ошибку еще до коммита.
Кстати, паттерн-матчинг работает не только в `switch`.
if (obj instanceof String s && s.length() > 5) {
System.out.println(s.toUpperCase());
}
4. Switch перестал быть просто оператором перехода. Во-первых, он стал выражением (возвращает значение, пример выше). Во-вторых, появилась стрелка:
switch (day) {
case MONDAY, FRIDAY, SUNDAY -> numLetters = 6;
case TUESDAY -> numLetters = 7;
default -> {
System.out.println("Выходной вычислен...");
yield 8;
}
}
5. Многострочные строки: Text Blocks
SQL-запросы, JSON, HTML прямо в коде без лесенки из кавычек и плюсов конкатенации:
String json = """
{
"user": "%s",
"active": %b
}
""".formatted(user.email(), user.active());
Три двойные кавычки сохраняют форматирование. Метод `.formatted()` (пришедший на смену `String.format()`) делает вставку переменных чистой и безопасной. Идеально для написания интеграционных тестов, где мокируемый ответ API пишется прямо над тестом.