Компактные конструкторы и проверка данных
Добавляйте логику проверки данных в компактные конструкторы, чтобы обеспечить целостность данных.
«Компактные конструкторы и проверка данных» — бесплатный урок 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 — локальная установка не требуется.
Все уроки этого курса
- Знакомство с записями
- Компактные конструкторы и проверка данных
- Пользовательские методы в записях
- Записи, классы и Lombok