0Pricing
Java Academy · Урок

Компактные конструкторы и проверка данных

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

«Компактные конструкторы и проверка данных» — бесплатный урок Java Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Java Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Java Academy содержит 4 уроков всего.

Компактные конструкторы

Компактный конструктор записи выполняется до присваивания компонентов. Он позволяет проверять или нормализовать данные, не повторяя присваивания компонентов.

Обычный и компактный конструкторы

Обычный канонический конструктор явно присваивает значения компонентам. В компактном конструкторе список параметров и присваивания отсутствуют — они выполняются автоматически после тела конструктора.

// Standard canonical constructor
record Range(int min, int max) {
    Range(int min, int max) {
        if (min > max) throw new IllegalArgumentException("min > max");
        this.min = min;  // explicit assignment
        this.max = max;
    }
}

// Compact constructor (same behavior, less code)
record Range2(int min, int max) {
    Range2 {  // no parameter list, no assignments
        if (min > max) throw new IllegalArgumentException("min > max");
        // components are assigned automatically after this block
    }
}

Проверка данных в компактных конструкторах

Компактные конструкторы — стандартный способ проверять данные записи. Выбрасывайте исключения как можно раньше, чтобы обеспечить выполнение инвариантов.

record Email(String address) {
    Email {
        if (address == null || address.isBlank())
            throw new IllegalArgumentException("Email cannot be blank");
        if (!address.contains("@"))
            throw new IllegalArgumentException("Invalid email: " + address);
        address = address.toLowerCase().strip(); // normalize (Java 16+)
    }
}

Email e = new Email("  Alice@Example.com  ");
System.out.println(e.address()); // alice@example.com

try { new Email("not-an-email"); }
catch (IllegalArgumentException ex) { System.out.println(ex.getMessage()); }

Нормализация в компактных конструкторах

В компактном конструкторе можно изменить переменные компонентов до их присваивания. Это нормализует данные при создании записи.

record PersonName(String firstName, String lastName) {
    PersonName {
        firstName = capitalize(firstName);
        lastName  = capitalize(lastName);
    }

    private static String capitalize(String s) {
        if (s == null || s.isEmpty()) return s;
        return Character.toUpperCase(s.charAt(0)) +
               s.substring(1).toLowerCase();
    }

    public String fullName() { return firstName + " " + lastName; }
}

PersonName name = new PersonName("jOHN", "DOE");
System.out.println(name.fullName()); // John Doe

Защитное копирование

Для изменяемых компонентов, таких как массивы или коллекции, создавайте защитную копию в компактном конструкторе, чтобы сохранить неизменяемость.

import java.util.*;

record Snapshot(List<String> items) {
    Snapshot {
        items = List.copyOf(items); // defensive copy — unmodifiable
    }
}

List<String> mutable = new ArrayList<>(List.of("a", "b", "c"));
Snapshot snap = new Snapshot(mutable);
mutable.add("d"); // doesn't affect snapshot
System.out.println(snap.items()); // [a, b, c]

try {
    snap.items().add("e"); // UnsupportedOperationException
} catch (UnsupportedOperationException e) {
    System.out.println("Snapshot is truly immutable!");
}

Запись Range с границами

Практическая запись Range, проверяющая условие min <= max и предоставляющая полезные вспомогательные методы.

record Range(int min, int max) {
    Range {
        if (min > max) throw new IllegalArgumentException(
            "min (" + min + ") must be <= max (" + max + ")");
    }

    public boolean contains(int value) { return value >= min && value <= max; }
    public int size()  { return max - min; }
    public int clamp(int value) { return Math.max(min, Math.min(max, value)); }
}

Range valid = new Range(1, 10);
System.out.println(valid.contains(5));  // true
System.out.println(valid.clamp(15));    // 10
System.out.println(valid.size());       // 9

Цепочка логики компактного конструктора

Для сложной проверки вынесите вспомогательные методы и вызывайте их из компактного конструктора.

record CreditCard(String number, String cvv, int expiryMonth, int expiryYear) {
    CreditCard {
        validateNumber(number);
        validateCvv(cvv);
        validateExpiry(expiryMonth, expiryYear);
        number = number.replaceAll("[^0-9]", ""); // strip spaces/dashes
    }

    private static void validateNumber(String n) {
        String digits = n.replaceAll("[^0-9]", "");
        if (digits.length() < 13 || digits.length() > 19)
            throw new IllegalArgumentException("Invalid card number length");
    }
    private static void validateCvv(String cvv) {
        if (!cvv.matches("[0-9]{3,4}"))
            throw new IllegalArgumentException("Invalid CVV");
    }
    private static void validateExpiry(int m, int y) {
        if (m < 1 || m > 12) throw new IllegalArgumentException("Invalid month");
        if (y < 2024)         throw new IllegalArgumentException("Card expired");
    }
}

Несколько шаблонов компактных конструкторов

Распространённые шаблоны проверки, используемые в компактных конструкторах.

record Percentage(double value) {
    Percentage {
        if (value < 0 || value > 100)
            throw new IllegalArgumentException(
                "Percentage must be 0-100, got: " + value);
        value = Math.round(value * 100.0) / 100.0; // round to 2 dp
    }

    public double asFraction() { return value / 100.0; }
}

Percentage tax = new Percentage(8.756);
System.out.println(tax.value());       // 8.76
System.out.println(tax.asFraction()); // 0.0876

Неканонические конструкторы

У записей могут быть дополнительные конструкторы, но они должны делегировать вызов каноническому конструктору с помощью this(...).

record Point(double x, double y) {
    // Non-canonical constructor: origin point
    Point() { this(0.0, 0.0); }

    // Non-canonical: polar coordinates
    static Point fromPolar(double r, double theta) {
        return new Point(r * Math.cos(theta), r * Math.sin(theta));
    }

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

Point origin = new Point();
Point p = Point.fromPolar(5, Math.PI/4);
System.out.printf("Distance: %.2f%n", origin.distance(p)); // 5.00

Запись с неизменяемой коллекцией

Запись, содержащая неизменяемый снимок пользовательских настроек, который проверяется и нормализуется при создании.

import java.util.*;

record UserPreferences(String theme, Set<String> enabledFeatures, int fontSize) {
    private static final Set<String> VALID_THEMES = Set.of("light", "dark", "system");
    private static final Set<String> VALID_FEATURES = Set.of("ai", "beta", "analytics");

    UserPreferences {
        if (!VALID_THEMES.contains(theme))
            throw new IllegalArgumentException("Unknown theme: " + theme);
        if (!VALID_FEATURES.containsAll(enabledFeatures))
            throw new IllegalArgumentException("Unknown feature in: " + enabledFeatures);
        if (fontSize < 10 || fontSize > 24)
            throw new IllegalArgumentException("fontSize must be 10-24");
        enabledFeatures = Set.copyOf(enabledFeatures); // defensive copy
    }
}

Ограничения компактных конструкторов

Что нельзя делать в компактном конструкторе:

  • Нельзя явно присваивать значения компонентам (они присваиваются автоматически после выполнения блока)
  • Нельзя вызывать this() или super()
  • Выбрасывание исключения предотвращает присваивание всех компонентов
record Safe(int value) {
    Safe {
        // CAN: validate and modify component variables
        if (value < 0) value = 0; // normalized to 0 if negative
        // value = this.value; // NOT NEEDED — assignment happens after block
    }
}

System.out.println(new Safe(-5).value());  // 0 (normalized)
System.out.println(new Safe(10).value());  // 10

Быстрая проверка

Что происходит с изменениями переменных компонентов, внесёнными в компактном конструкторе?

Итоги: компактные конструкторы и проверка данных

Главное:

  • Компактные конструкторы выполняются до присваивания компонентов — явные присваивания не нужны
  • Используйте их для проверки, нормализации или защитного копирования компонентов
  • Изменяйте переменные компонентов, чтобы нормализовать значения: перевести в нижний регистр, удалить пробелы, создать копию
  • Выбрасывайте IllegalArgumentException для некорректных данных, чтобы обеспечить выполнение инвариантов
  • Используйте List.copyOf / Set.copyOf для компонентов изменяемых коллекций
  • Неканонические конструкторы должны делегировать вызов каноническому с помощью this(...)

Часто задаваемые вопросы

Урок «Компактные конструкторы и проверка данных» бесплатный?

Да — полный текст урока «Компактные конструкторы и проверка данных» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Java Academy, подпишись на CoddyKit PRO. Курс Java Academy содержит 4 уроков всего.

Чему я научусь в уроке «Компактные конструкторы и проверка данных»?

Добавляйте логику проверки данных в компактные конструкторы, чтобы обеспечить целостность данных. Ты практикуешь Java Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Java Academy?

Предыдущий опыт не требуется. Java Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.

Сколько времени занимает урок «Компактные конструкторы и проверка данных»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Java Academy?

Да. Каждый урок Java Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Знакомство с записями
  2. Компактные конструкторы и проверка данных
  3. Пользовательские методы в записях
  4. Записи, классы и Lombok
← Назад к Java Academy