Menu
Coddy logo textTech

Records (Java 16+)

Часть раздела Объектно-ориентированное программирование путешествия по Java на Coddy — урок 51 из 87.

При создании простых классов для передачи данных вы часто пишете повторяющийся шаблонный код: приватные поля, конструктор, геттеры, toString(), equals() и hashCode(). В Java 16 появились records, чтобы избавиться от этой рутины.

Запись — это специальный класс, предназначенный для хранения неизменяемых данных. Вы объявляете её с помощью ключевого слова record, за которым следует список компонентов:

record Person(String name, int age) { }

// Эта единственная строка автоматически предоставляет:
// - private final поля: name и age
// - конструктор: Person(String name, int age)
// - методы доступа: name() и age()
// - toString(), equals(), и hashCode()

Использование записи — это просто:

Person p = new Person("Alice", 30);
System.out.println(p.name());    // Alice
System.out.println(p.age());     // 30
System.out.println(p);           // Person[name=Alice, age=30]

Обратите внимание, что методы доступа не используют префикс get — они просто называются так же, как и поля. Записи (records) неявно являются final, поэтому их нельзя расширять. Все поля также являются final, что делает записи неизменяемыми по своей природе.

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

record Person(String name, int age) {
    public Person {
        if (age < 0) throw new IllegalArgumentException("Age cannot be negative");
    }
}

Записи идеально подходят для DTO, ответов API или любого сценария, где вам нужен простой неизменяемый контейнер данных без написания шаблонного кода.

challenge icon

Задание

Легко

Давайте создадим систему бронирования билетов в кино, которая продемонстрирует элегантность записей (records) в Java. Вы увидите, как записи устраняют шаблонный код, позволяя при этом добавлять пользовательские методы и валидацию.

Вы организуете свой код в трех файлах:

  • Movie.java: Создайте запись, представляющую фильм, с тремя компонентами: title (String), genre (String) и durationMinutes (int). Добавьте пользовательский метод getFormattedDuration(), который возвращает длительность в удобном формате: [hours]h [minutes]m (например, 142 минуты превращаются в 2h 22m). Используйте целочисленное деление и остаток от деления для вычисления часов и оставшихся минут.
  • Ticket.java: Создайте запись, представляющую билет в кино, с тремя компонентами: movie (Movie), seatNumber (String) и price (double). Добавьте компактный конструктор, который проверяет цену — если цена меньше или равна нулю, выбросьте исключение IllegalArgumentException с сообщением "Price must be positive". Также добавьте пользовательский метод printTicket(), который возвращает многострочную строку:
    ========== TICKET ==========
    Movie: [movie title]
    Genre: [movie genre]
    Duration: [formatted duration from movie]
    Seat: [seatNumber]
    Price: $[price]
    ============================
  • Main.java: Объедините вашу систему бронирования! Вы получите пять входных данных: название фильма (String), жанр (String), длительность в минутах (int), номер места (String) и цена билета (double).

    Создайте запись Movie с первыми тремя входными данными, затем создайте запись Ticket с фильмом, номером места и ценой. Выведите результат вызова printTicket() для вашего билета.

Вы получите пять входных данных по порядку: название фильма, жанр, длительность (минуты), номер места и цена.

Обратите внимание, насколько чище записи по сравнению с традиционными классами — нет необходимости писать конструкторы, геттеры, toString(), equals() или hashCode(). Компактный конструктор позволяет добавлять валидацию без повторного присваивания полей, и вы все равно можете добавлять пользовательские методы при необходимости!

Шпаргалка

Java 16 представила записи (records), чтобы исключить шаблонный код для простых классов, переносящих данные. Запись — это специальный класс, предназначенный для хранения неизменяемых данных.

Объявите запись с помощью ключевого слова record, за которым следует список компонентов:

record Person(String name, int age) { }

Это автоматически обеспечивает:

  • Приватные финальные поля
  • Конструктор
  • Методы доступа (без префикса get)
  • toString(), equals(), и hashCode()

Использование записи:

Person p = new Person("Alice", 30);
System.out.println(p.name());    // Alice
System.out.println(p.age());     // 30
System.out.println(p);           // Person[name=Alice, age=30]

Записи неявно являются final и не могут быть расширены. Все поля являются final, что делает записи неизменяемыми по своей природе.

Добавьте валидацию с помощью компактного конструктора:

record Person(String name, int age) {
    public Person {
        if (age < 0) throw new IllegalArgumentException("Age cannot be negative");
    }
}

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

record Person(String name, int age) {
    public String getInfo() {
        return name + " is " + age + " years old";
    }
}

Попробуйте сами

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // Чтение входных данных
        String title = scanner.nextLine();
        String genre = scanner.nextLine();
        int durationMinutes = Integer.parseInt(scanner.nextLine());
        String seatNumber = scanner.nextLine();
        double price = Double.parseDouble(scanner.nextLine());
        
        // TODO: Создайте запись Movie с полями title, genre и durationMinutes
        
        // TODO: Создайте запись Ticket с полями movie, seatNumber и price
        
        // TODO: Выведите билет, используя метод printTicket()
        
    }
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование