JavaRush /Курсы /JAVA 25 SELF /Record с методами

Record с методами

JAVA 25 SELF
22 уровень , 3 лекция
Открыта

1. Расширение record-класса: дополнительные методы

Можно ли добавить методы в record?

Конечно! Record — это как квартира с готовым ремонтом: стены и пол уже сделаны, менять их нельзя, но мебель расставить по-своему никто не запрещает. Внутри record можно объявлять обычные методы, статические методы и даже хранить константы. Это значит, что бизнес-логику не обязательно выносить в отдельные «утилитные» классы — её можно аккуратно встроить прямо в сам record.

Пример: метод вычисления расстояния между точками

Допустим, у нас есть record для точки на плоскости:


public record Point(int x, int y) {
    // Дополнительный метод
    public double distanceTo(Point other) {
        int dx = this.x - other.x;
        int dy = this.y - other.y;
        return Math.sqrt(dx * dx + dy * dy);
    }
}

Теперь можно делать так:


Point p1 = new Point(0, 0);
Point p2 = new Point(3, 4);
System.out.println(p1.distanceTo(p2)); // 5.0

Как видите, record-класс можно «обогащать» своими методами — и это очень удобно!

Пример: статический метод


public record Rectangle(int width, int height) {
    public int area() {
        return width * height;
    }

    public static Rectangle square(int size) {
        return new Rectangle(size, size);
    }
}

Теперь можно создавать «квадрат» одним вызовом:


Rectangle r = Rectangle.square(5);
System.out.println(r.area()); // 25

2. Компактный конструктор и валидация данных

Зачем нужен “компактный” конструктор?

Канонический record-конструктор создаётся автоматически и присваивает параметры полям. Но иногда хочется добавить проверку входных данных (например, запретить отрицательные координаты).

В обычном классе мы бы писали:


public class Point {
    private final int x;
    private final int y;

    public Point(int x, int y) {
        if (x < 0 || y < 0) throw new IllegalArgumentException();
        this.x = x;
        this.y = y;
    }
    // ...
}

В record-классе можно объявить компактный конструктор — без повторения списка параметров и без явного присваивания полей (это делает за нас компилятор).

Синтаксис компактного конструктора


public record Point(int x, int y) {
    public Point {
        if (x < 0 || y < 0) {
            throw new IllegalArgumentException("Coordinates must be non-negative");
        }
        // Не нужно писать: this.x = x; this.y = y;
    }
}
  • Параметры конструктора автоматически совпадают с компонентами record.
  • Присваивание this.x = x и this.y = y делается компилятором автоматически после выполнения тела конструктора (или после успешного выхода из него).
  • Если бросить исключение, объект не будет создан.

Пример с проверкой


Point p1 = new Point(3, 5); // OK
Point p2 = new Point(-1, 2); // Бросит IllegalArgumentException!

Можно ли объявить “обычный” конструктор?

Да, можно! Если нужно создать конструктор с другим списком параметров или дополнительной логикой, объявляйте его явно:


public record Range(int from, int to) {
    public Range(int size) {
        this(0, size); // вызывает основной конструктор
    }
}

3. Ограничения record-классов

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

Только компоненты — никаких дополнительных нестатических полей

В record-классе нельзя объявлять новые нестатические поля:


public record Person(String name, int age) {
    // int id; // Ошибка компиляции! Нельзя добавлять нестатические поля.
}

Можно объявлять статические поля и методы:


public record Person(String name, int age) {
    public static final String SPECIES = "Homo sapiens";
}

Record всегда final

Record-класс не может быть родительским (нельзя от него наследоваться) и сам не может явно наследовать другой класс (кроме неявного наследования от java.lang.Record). Это значит, что record-класс — всегда «конечная» структура.


public record User(String login) { }

// public class Admin extends User {} // Ошибка: нельзя наследоваться от record!

Можно реализовывать интерфейсы

Record-класс может реализовывать интерфейсы:


public interface Printable {
    void print();
}

public record Invoice(int amount) implements Printable {
    @Override
    public void print() {
        System.out.println("Сумма: " + amount);
    }
}

4. Примеры: расширенные records в реальных задачах

Давайте рассмотрим несколько практических примеров, где дополнительные методы и компактные конструкторы делают record-класс действительно полезным.

Record с вычисляемым методом


public record Circle(double x, double y, double radius) {
    public double area() {
        return Math.PI * radius * radius;
    }

    public double distanceTo(Circle other) {
        double dx = x - other.x;
        double dy = y - other.y;
        return Math.sqrt(dx * dx + dy * dy);
    }
}

Record с валидацией


public record Email(String value) {
    public Email {
        if (value == null || !value.contains("@")) {
            throw new IllegalArgumentException("Некорректный email: " + value);
        }
    }
}

Теперь нельзя создать некорректный email:


Email e1 = new Email("test@example.com"); // OK
Email e2 = new Email("not-an-email"); // Бросит IllegalArgumentException

Record с дополнительными статическими методами


public record Temperature(double celsius) {
    public static Temperature fromFahrenheit(double fahrenheit) {
        return new Temperature((fahrenheit - 32) * 5 / 9);
    }

    public double toFahrenheit() {
        return celsius * 9 / 5 + 32;
    }
}

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


Temperature t = Temperature.fromFahrenheit(98.6);
System.out.println(t.celsius()); // 37.0
System.out.println(t.toFahrenheit()); // 98.6

5. Компактный конструктор: нюансы и ограничения

Когда использовать компактный конструктор?

  • Если нужно проверить корректность данных (валидация).
  • Если нужно изменить значения перед сохранением (например, округлить число или привести строку к верхнему регистру).
  • Если хочется избежать дублирования списка параметров.

Особенности работы

  • В компактном конструкторе нельзя явно присваивать значения компонентам (this.x = ...) — это вызовет ошибку компиляции, так как компилятор сам выполняет присваивание после выполнения тела конструктора.
  • В компактном конструкторе нельзя менять имена параметров — они всегда совпадают с именами компонентов record.

Пример: автоматическое округление


public record Money(double amount) {
    public Money {
        amount = Math.round(amount * 100) / 100.0; // Округляем до копеек
    }
}

6. Практика: развиваем учебное приложение

Допустим, мы делаем банковские операции в учебном приложении. Пусть у нас есть record-класс Transaction, который хранит сумму, отправителя и получателя.


public record Transaction(String from, String to, double amount) {
    public Transaction {
        if (amount <= 0) throw new IllegalArgumentException("Сумма должна быть положительной");
        if (from == null || to == null) throw new IllegalArgumentException("Поля не могут быть null");
    }

    public String description() {
        return String.format("Перевод %.2f от %s к %s", amount, from, to);
    }
}

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


Transaction t = new Transaction("Alice", "Bob", 150.0);
System.out.println(t.description()); // Перевод 150.00 от Alice к Bob

Попытка создать некорректную транзакцию вызовет ошибку:


Transaction t2 = new Transaction("Alice", "Bob", -10.0); // IllegalArgumentException

Таблица: что можно и что нельзя в record-классе

Можно в record-классе Нельзя в record-классе
Обычные методы Новые нестатические поля
Статические методы и поля Наследоваться от других классов
Реализовывать интерфейсы Быть суперклассом для других
Компактные и обычные конструкторы Изменять компоненты после создания
Переопределять методы Использовать сеттеры

7. Типичные ошибки при работе с record-классами с нестандартным телом

Ошибка № 1: попытка добавить нестатическое поле.
Новички часто пытаются добавить в record-класс «ещё одно поле для внутренней логики» — например, счётчик или кэш. Это не сработает: компилятор тут же выдаст ошибку. Если вам нужно хранить дополнительное состояние, скорее всего, record-класс — не ваш выбор.

Ошибка № 2: забыли валидацию в компактном конструкторе.
Если вы хотите, чтобы объект всегда был валидным, делайте проверку в компактном конструкторе. Не полагайтесь на то, что «пользователь сам не введёт ерунду».

Ошибка № 3: попытка изменить компонент после создания.
Поля record-класса final — их нельзя изменить ни напрямую, ни через методы. Если вам нужна изменяемая структура — используйте обычный класс.

Ошибка № 4: дублирование логики в методах и конструкторе.
Иногда логику проверки и логику вычислений пытаются дублировать и в методах, и в конструкторе. Лучше делать всю валидацию в конструкторе, а методы оставлять для «чистой» бизнес-логики.

Ошибка № 5: забыли про ограничения наследования.
Record-класс всегда final — нельзя создать от него наследника. Если вы проектируете иерархию, где нужны подклассы — используйте обычные классы.

1
Задача
JAVA 25 SELF, 22 уровень, 3 лекция
Недоступна
Показания термометра на метеостанции 🌡️
Показания термометра на метеостанции 🌡️
1
Задача
JAVA 25 SELF, 22 уровень, 3 лекция
Недоступна
Валидация электронного адреса при регистрации 📧
Валидация электронного адреса при регистрации 📧
1
Задача
JAVA 25 SELF, 22 уровень, 3 лекция
Недоступна
Создание карточки персонажа с общими данными 🧍
Создание карточки персонажа с общими данными 🧍
1
Задача
JAVA 25 SELF, 22 уровень, 3 лекция
Недоступна
Регистрация финансовых транзакций в банке 💸
Регистрация финансовых транзакций в банке 💸
Комментарии (1)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
I'll kick them all Уровень 5
28 сентября 2025
level22.task14 Ну вообще-то не нужно проверять на пустую строку, если дальше проверяется наличие символа @. Просто логически.