Версия Java 17 была выпущена не так уж давно. Отличие этого релиза в том, что это  —  новая TLS-версия (Long Term Support, с долговременной поддержкой) после Java 11.

В этой статье рассмотрим новые практические функции, которые были введены между 11-ой и 17-ой версиями.

  1. Switch-выражения.
  2. Текстовые блоки.
  3. Сопоставление с образцом (Pattern Matching) для instanceof.
  4. Полезные NullPointerException.
  5. Записи (Records).
  6. Запечатанные (sealed) классы.
  7. Сопоставление с образцом для switch.

Switch-выражения

Switch-выражения  —  это оператор switch с улучшенным синтаксисом и функциональностью. Как они работают?

Выведем информацию о том, является ли данный день будним или выходным. При использовании операторов switch это выглядит так:

DayOfWeek dayOfWeek = // назначение значений
switch (dayOfWeek) {
case SUNDAY:
case SATURDAY:
System.out.println("Weekend");
break;
case FRIDAY:
case THURSDAY:
case WEDNESDAY:
case TUESDAY:
case MONDAY:
System.out.println("Weekday");
break;
default:
System.out.println("Unknown Day!");
}

С помощью новых switch-выражений этот код можно переписать так:

System.out.println(switch (day) {
case SUNDAY, SATURDAY -> "Weekend";
case FRIDAY, THURSDAY, WEDNESDAY, TUESDAY, MONDAY -> "Weekday";
});

В чем разница?

Во-первых, теперь можно определять более одного условия для одного и тот же случая.

Во-вторых, больше не нужно использовать ключевое слово break, чтобы остановить выполнение. При использовании switch-выражений выполняется только правая часть соответствующего случая, если применяется синтаксис со стрелкой (->).

В-третьих, поскольку операторы switch стали switch-выражениями, а выражения вычисляют значение, то теперь они могут возвращать значение.

В-четвертых, выражения switch являются исчерпывающими. Если вы забудете указать случай в выражении switch, то получите ошибку во время компиляции. Если вы охватываете все случаи, вам не нужно иметь случай “по умолчанию”.

В-пятых, если необходимо выполнить блок кода, то поддерживается следующее:

System.out.println(switch (day) {
case SUNDAY, SATURDAY -> "Weekend";
case FRIDAY, THURSDAY, WEDNESDAY, TUESDAY, MONDAY -> {
// какая-то логика
yield "Weekday";
}
});

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

Текстовые блоки

Текстовые блоки  —  это просто многострочные строковые литералы.

Чтобы поместить в код такой JSON:

{
"name":"fatih",
"surname":"iver",
"birthYear":1996
}

Нужно отформатировать его таким образом:

String json = "{n" +
" "name":"fatih",n" +
" "surname":"iver",n" +
" "birthYear":1996n" +
"}";

С помощью текстовых блоков можно просто написать следующее:

String json = """
{
"name":"fatih",
"surname":"iver",
"birthYear":1996
}
""";

С текстовыми блоками больше не нужно экранировать кавычки или объединять несколько линий, чтобы сформировать одну строку.

Особенно это облегчает жизнь, если вы храните HTML, JSON, SQL и XML в исходном коде.

Новые текстовые блоки делают код более красивым и простым для чтения.

Сопоставление с образцом (Pattern Matching) для instanceof

instanceof позволяет проверить, принадлежит ли объект к нужному типу. Если это так, мы приводим этот объект к желаемому типу, чтобы вызвать метод, доступный только для этого типа. Пример:

Object o = "Hello, World!";

if (o instanceof String) {
    String s = (String) o;
    System.out.println(s.length());
}

При сопоставлении с образцом не нужно явное приведение типов:

Object o = "Hello, World!";

if (o instanceof String s) {
    System.out.println(s.length());
}

Вы даже можете использовать переменную s, как это показано ниже, если выражение instanceof принимает значение true:

Object o = "Hello, World!";

if (o instanceof String s && !s.isBlank()) {
    System.out.println(s.toLowerCase());
}

Полезные NullPointerException

При выполнении следующего кода:

Map<String, String> map = new HashMap<>();
System.out.println(map.get("key").toLowerCase());

Мы получим:

Exception in thread "main" java.lang.NullPointerException
at Main.example(Main.java:14)
at Main.main(Main.java:10)

Фрагмент лишь сообщает, в какой строке произошло исключение. При этом ничего не сказано о том, приняла ли карта значение null или же в ней не оказалось нужного ключа.

С новым улучшением мы получаем:

Exception in thread "main" java.lang.NullPointerException: 
Cannot invoke "String.toLowerCase()" because the return value of "java.util.Map.get(Object)" is null
at helpful_null_pointer_exception.Main.example(Main.java:15)
at helpful_null_pointer_exception.Main.main(Main.java:9)

Сообщение говорит, где и почему произошло NPE.

Записи

Чтобы определить неизменяемый класс, можно действовать так:

public class Person {
private final String name;
private final String surname;
private final Integer age;
public Person(String name, String surname, Integer age) {
this.name = name;
this.surname = surname;
this.age = age;
}
public String getName() {
return name;
}
public String getSurname() {
return surname;
}
public Integer getAge() {
return age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
if (!Objects.equals(name, person.name)) return false;
if (!Objects.equals(surname, person.surname)) return false;
return Objects.equals(age, person.age);
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (surname != null ? surname.hashCode() : 0);
result = 31 * result + (age != null ? age.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + ''' +
", surname='" + surname + ''' +
", age=" + age +
'}';
}
}

Получается довольно много строк. Упростим синтаксис, воспользовавшись библиотекой Lombok:

@Value
public class Person {
String name;
String surname;
Integer age;
}

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

public record Person(String name, String surname, Integer age) {
}

Тип record создает неизменяемый класс с приватными финализированными полями, геттерами, методом toString, а также методами equals и hashCode.

Таким образом, есть три разных способа добиться одного и того же. В первом подходе мы были слишком многословны, а во втором  —  зависели от стороннего решения. При третьем код намного короче, и мы не зависим от стороннего решения.

Запечатанные классы

При помощи этой функции класс может указывать, какие классы могут его расширять. Например:

public abstract sealed class Developer permits BackendDeveloper, FrontendDeveloper {
}

Следующий код не будет компилироваться с приведенным выше определением:

public final class ProductManager extends Developer {
}

Этот код не компилируется, потому что класс Developer разрешает расширять себя только классам BackendDeveloper и FrontendDeveloper. Однако его пытается расширить класс ProductManager. И возникает ошибка компиляции.

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

Сопоставление с образцом для switch

Для этого объяснения используется пример с сайта OpenJDK.

Вот код:

static String formatter(Object o) {
String formatted = "unknown";
if (o instanceof Integer i) {
formatted = String.format("int %d", i);
} else if (o instanceof Long l) {
formatted = String.format("long %d", l);
} else if (o instanceof Double d) {
formatted = String.format("double %f", d);
} else if (o instanceof String s) {
formatted = String.format("String %s", s);
}
return formatted;
}

При сопоставлении с образцом switch:

static String formatterPatternSwitch(Object o) {
return switch (o) {
case Integer i -> String.format("int %d", i);
case Long l -> String.format("long %d", l);
case Double d -> String.format("double %f", d);
case String s -> String.format("String %s", s);
default -> o.toString();
};
}

Мы сопоставили объект на основе его типа и не выполняли никакого явного приведения.

Эту функцию можно объединить с запечатанными классами. Так получится писать точные и довольно емкие switch-выражения.

Бонус

До Java 16 мы собирали потоки в виде списка, как показано ниже:

List<Integer> digits = List.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
List<Integer> evenDigits = digits.stream()
.filter(digit -> digit % 2 == 0)
.collect(Collectors.toList());

Теперь для той же цели можно вызвать новый метод ToList для потоков:

List<Integer> digits = List.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
List<Integer> evenDigits = digits.stream()
        .filter(digit -> digit % 2 == 0)
        .toList();

Читайте также:

Читайте нас в Telegram, VK и Дзен

Перевод статьи Fatih İver: 7 Practical Java Enhancements from Java 11 to Java 17

Сообщение Java 17: что нового по сравнению с Java 11 появились сначала на NOP::Nuances of programming.

Похожие записи

AEGIS Algorithms Android Angular Apache Airflow Apache Druid Apache Flink Apache Spark API API Canvas AppSec Architecture Artificial Intelligence Astro Authentication Authorization AutoGPT AWS AWS Aurora AWS Boto3 AWS EC2 AWS Lambda Azure Babylon.js Backend bash Beautiful Soup Bento UI Big Data Binary Tree Browser API Bun Career Cassandra Charts ChatGPT Chrome Extension Clean Code CLI ClickHouse Coding Codux Combine Compose Computer Context Fusion Copilot Cosmo Route CProgramming cron Cryptography CSS CTF Cypress DALL-E Data Analysis Data science Database dbt dbt Cloud deno Design Design Patterns Detekt Development Distributed Systems Django Docker Docker Hub Drizzle DRY DuckDB Express FastAPI Flask Flutter For Beginners Front End Development Game Development GCN GCP Geospatial Git GitHub Actions GitHub Pages Gitlab GMS GoFr Golang Google Google Sheets Google Wire GPT-3 GPT3 Gradio Gradle Grafana Graphic Design GraphQL gRPC Guidance HMS Hotwire HTML Huawei HuggingFace IndexedDB InfoSec Interview iOS Jackknife Java JavaScript Jetpack Compose JSON Kafka Kotlin Kubernetes LangChain Laravel Linux LlaMA LLM localStorage Logging Machine Learning Magento Math Mermaid Micro Frontends Mobile Mobile App Development mondayDB MongoDB Mongoose MySQL Naming NestJS NET NetMock Networks NextJS NLP Node.js Nodejs NoSQL NPM OOP OpenAI OTP Pandas PDF PHP Playwright Plotly Polars PostgreSQL Prefect Productivity Programming Prometheus Puppeteer Pushover Python Pytorch Quarkus Rabbitmq RAG Ramda Raspberry Pi React React Native Reactor Redis REST API Revolut Riverpod RProgramming Ruby Ruby on Rails Rust Scalene SCDB ScyllaDB Selenium Servers Sklearn SLO SnowFlake Snowkase Software Architecture Software Development Solara Solid Spring Boot SQL SQLite Streamlit SudoLang Supabase Swift SwiftUI Tailwind CSS Taipy Terraform Testing Transformers TURN TypeScript Ubuntu UI Design Unix UX UX Design Vim Vite VSCode Vue Web Architecture Web Components Web Development Web Frameworks Web Scraping Web-разработка Webassembly Websocket Whisper Widgets WordPress YAML YouTube Наука о данных Разное Тренды

Go — единственный выбор для бэкенд-разработчика?

Вы пропустили

AEGIS Algorithms Android Angular Apache Airflow Apache Druid Apache Flink Apache Spark API API Canvas AppSec Architecture Artificial Intelligence Astro Authentication Authorization AutoGPT AWS AWS Aurora AWS Boto3 AWS EC2 AWS Lambda Azure Babylon.js Backend bash Beautiful Soup Bento UI Big Data Binary Tree Browser API Bun Career Cassandra Charts ChatGPT Chrome Extension Clean Code CLI ClickHouse Coding Codux Combine Compose Computer Context Fusion Copilot Cosmo Route CProgramming cron Cryptography CSS CTF Cypress DALL-E Data Analysis Data science Database dbt dbt Cloud deno Design Design Patterns Detekt Development Distributed Systems Django Docker Docker Hub Drizzle DRY DuckDB Express FastAPI Flask Flutter For Beginners Front End Development Game Development GCN GCP Geospatial Git GitHub Actions GitHub Pages Gitlab GMS GoFr Golang Google Google Sheets Google Wire GPT-3 GPT3 Gradio Gradle Grafana Graphic Design GraphQL gRPC Guidance HMS Hotwire HTML Huawei HuggingFace IndexedDB InfoSec Interview iOS Jackknife Java JavaScript Jetpack Compose JSON Kafka Kotlin Kubernetes LangChain Laravel Linux LlaMA LLM localStorage Logging Machine Learning Magento Math Mermaid Micro Frontends Mobile Mobile App Development mondayDB MongoDB Mongoose MySQL Naming NestJS NET NetMock Networks NextJS NLP Node.js Nodejs NoSQL NPM OOP OpenAI OTP Pandas PDF PHP Playwright Plotly Polars PostgreSQL Prefect Productivity Programming Prometheus Puppeteer Pushover Python Pytorch Quarkus Rabbitmq RAG Ramda Raspberry Pi React React Native Reactor Redis REST API Revolut Riverpod RProgramming Ruby Ruby on Rails Rust Scalene SCDB ScyllaDB Selenium Servers Sklearn SLO SnowFlake Snowkase Software Architecture Software Development Solara Solid Spring Boot SQL SQLite Streamlit SudoLang Supabase Swift SwiftUI Tailwind CSS Taipy Terraform Testing Transformers TURN TypeScript Ubuntu UI Design Unix UX UX Design Vim Vite VSCode Vue Web Architecture Web Components Web Development Web Frameworks Web Scraping Web-разработка Webassembly Websocket Whisper Widgets WordPress YAML YouTube Наука о данных Разное Тренды

Go — единственный выбор для бэкенд-разработчика?

AEGIS Algorithms Android Angular Apache Airflow Apache Druid Apache Flink Apache Spark API API Canvas AppSec Architecture Artificial Intelligence Astro Authentication Authorization AutoGPT AWS AWS Aurora AWS Boto3 AWS EC2 AWS Lambda Azure Babylon.js Backend bash Beautiful Soup Bento UI Big Data Binary Tree Browser API Bun Career Cassandra Charts ChatGPT Chrome Extension Clean Code CLI ClickHouse Coding Codux Combine Compose Computer Context Fusion Copilot Cosmo Route CProgramming cron Cryptography CSS CTF Cypress DALL-E Data Analysis Data science Database dbt dbt Cloud deno Design Design Patterns Detekt Development Distributed Systems Django Docker Docker Hub Drizzle DRY DuckDB Express FastAPI Flask Flutter For Beginners Front End Development Game Development GCN GCP Geospatial Git GitHub Actions GitHub Pages Gitlab GMS GoFr Golang Google Google Sheets Google Wire GPT-3 GPT3 Gradio Gradle Grafana Graphic Design GraphQL gRPC Guidance HMS Hotwire HTML Huawei HuggingFace IndexedDB InfoSec Interview iOS Jackknife Java JavaScript Jetpack Compose JSON Kafka Kotlin Kubernetes LangChain Laravel Linux LlaMA LLM localStorage Logging Machine Learning Magento Math Mermaid Micro Frontends Mobile Mobile App Development mondayDB MongoDB Mongoose MySQL Naming NestJS NET NetMock Networks NextJS NLP Node.js Nodejs NoSQL NPM OOP OpenAI OTP Pandas PDF PHP Playwright Plotly Polars PostgreSQL Prefect Productivity Programming Prometheus Puppeteer Pushover Python Pytorch Quarkus Rabbitmq RAG Ramda Raspberry Pi React React Native Reactor Redis REST API Revolut Riverpod RProgramming Ruby Ruby on Rails Rust Scalene SCDB ScyllaDB Selenium Servers Sklearn SLO SnowFlake Snowkase Software Architecture Software Development Solara Solid Spring Boot SQL SQLite Streamlit SudoLang Supabase Swift SwiftUI Tailwind CSS Taipy Terraform Testing Transformers TURN TypeScript Ubuntu UI Design Unix UX UX Design Vim Vite VSCode Vue Web Architecture Web Components Web Development Web Frameworks Web Scraping Web-разработка Webassembly Websocket Whisper Widgets WordPress YAML YouTube Наука о данных Разное Тренды

Производительность в Jetpack Compose: стабильность и неизменяемость