Чи можна додавати додаткові методи в Record
Record дозволяє додавати: 4. Приватні методи (для внутрішньої логіки) 5. Конструктори (повинні викликати канонічний)
🟢 Junior Level
Так, можна! Record — це не просто набір полів. Ви можете додавати будь-які методи, але з деякими обмеженнями.
Що можна:
public record Money(BigDecimal amount, String currency) {
// ✅ Статичні методи
public static Money zero() {
return new Money(BigDecimal.ZERO, "USD");
}
// ✅ Додаткові методи
public Money add(Money other) {
if (!this.currency.equals(other.currency)) {
throw new IllegalArgumentException("Different currencies");
}
return new Money(this.amount.add(other.amount), this.currency);
}
// ✅ Приватні методи (Java 16+)
private void validate() {
if (amount.compareTo(BigDecimal.ZERO) < 0) {
throw new IllegalArgumentException("Negative amount");
}
}
}
Що не можна:
- ❌ Додавати instance поля (крім static)
- ❌ Робити Record mutable
🟡 Middle Level
Як це працює
Record дозволяє додавати:
- Статичні поля (
static fields) - Статичні методи (
static methods) - Instance методи (будь-якої видимості)
- Приватні методи (для внутрішньої логіки)
- Конструктори (повинні викликати канонічний)
Приклади:
public record User(String name, int age, String email) {
// ✅ Статичне поле
public static final int MAX_AGE = 150;
// ✅ Статична фабрика
public static User anonymous() {
return new User("Anonymous", 0, "");
}
// ✅ Instance метод
public boolean isAdult() {
return age >= 18;
}
// ✅ Приватний метод
private boolean isValidEmail() {
return email != null && email.contains("@");
}
// ✅ Канонічний конструктор з валідацією
public User {
if (age < 0 || age > MAX_AGE) {
throw new IllegalArgumentException("Invalid age");
}
}
}
Типові помилки
- Спроба додати instance поле:
public record BadRecord() { // ❌ Instance поле — помилка компіляції private int mutableField = 0; // ✅ Тільки static private static int counter = 0; } - Зміна повертаного типу аксесора:
public record Point(int x, int y) { // ❌ Не можна перевизначити аксесор з іншим типом public String x() { // compilation error return String.valueOf(x); } }
Практичне застосування
1. Value objects з логікою:
public record Range(int min, int max) {
public Range {
if (min > max) {
throw new IllegalArgumentException("min > max");
}
}
public boolean contains(int value) {
return value >= min && value <= max;
}
public Range intersect(Range other) {
return new Range(
Math.max(this.min, other.min),
Math.min(this.max, other.max)
);
}
}
2. Builder-подібні методи:
public record Query(String table, List<String> columns, String where) {
public Query(String table) {
this(table, List.of("*"), null);
}
public Query select(String... columns) {
return new Query(this.table, List.of(columns), this.where);
}
public Query where(String condition) {
return new Query(this.table, this.columns, condition);
}
}
// Використання
Query q = new Query("users")
.select("name", "email")
.where("age > 18");
🔴 Senior Level
Internal Implementation
Компілятор дозволяє:
- Додаткові методи не впливають на canonical конструктор
- Instance методи не змінюють структуру Record
- Статичні поля зберігаються в класі, а не в екземплярах
Обмеження на перевизначення:
public record User(String name) {
// Починаючи з Java 16+ МОЖНА перевизначити аксесор з тією ж сигнатурою.
// В preview-версіях це було заборонено.
public String name() { return name.toUpperCase(); } // OK в Java 16+
// ❌ Не можна перевизначити final методи з java.lang.Record
// Методи equals/hashCode/toString declared як abstract в java.lang.Record,
// але їх МОЖНА і потрібно перевизначати.
}
Архітектурні Trade-offs
Методи в Record vs окремі утиліти:
| Підхід | Плюси | Мінуси |
|---|---|---|
| Методи в Record | Логіка поруч з даними, зручніше | Record стає «товстішим» |
| Окремі утиліти | Record залишається простим | Більше файлів, імпортів |
Edge Cases
1. Перевизначення equals/hashCode:
public record CaseInsensitiveString(String value) {
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof CaseInsensitiveString other)) return false;
return Objects.equals(
this.value.toLowerCase(),
other.value.toLowerCase()
);
}
@Override
public int hashCode() {
return value.toLowerCase().hashCode();
}
}
2. Приватні конструктори:
public record UserId(UUID value) {
// ✅ Приватний конструктор (повинен викликати канонічний)
private UserId(String value) {
this(UUID.fromString(value));
}
public static UserId of(String value) {
return new UserId(value);
}
}
3. Generic методи в Record:
public record JsonNode(String json) {
public <T> T parse(Class<T> type) {
// парсинг JSON
return null;
}
}
Продуктивність
Додаткові методи:
- No overhead в пам'яті (методи в class, не в instance)
- JIT інлайнить методи як звичайно
- Різниці зі звичайним класом немає
Production Experience
Real-world приклад — Domain Events:
public record OrderCreatedEvent(
String orderId,
List<OrderItem> items,
Instant createdAt
) {
public BigDecimal totalAmount() {
return items.stream()
.map(item -> item.price().multiply(BigDecimal.valueOf(item.quantity())))
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
public boolean isHighValue() {
return totalAmount().compareTo(new BigDecimal("1000")) > 0;
}
public record OrderItem(String productId, int quantity, BigDecimal price) {}
}
Best Practices
// ✅ Додавайте бізнес-логіку в Record
public record Money(BigDecimal amount, Currency currency) {
public Money add(Money other) { /* ... */ }
public boolean isPositive() { return amount.compareTo(BigDecimal.ZERO) > 0; }
}
// ✅ Статичні фабрики для зручності
public record Point(double x, double y) {
public static Point fromPolar(double r, double theta) {
return new Point(r * Math.cos(theta), r * Math.sin(theta));
}
}
// ❌ Не робіть Record занадто «товстим»
// ❌ Не додавайте mutable стан
// ❌ Не перевизначайте аксесори
🎯 Шпаргалка для співбесіди
Обов’язково знати:
- В Record можна додавати static поля, static методи, instance методи
- Instance поля (не static) — заборонені, тільки final канонічні компоненти
- Приватні методи підтримуються (Java 16+)
- Конструктори повинні викликати канонічний через
this(...) - Можна перевизначити equals/hashCode/toString, але не обов’язково
- Аксесори можна перевизначити в Java 16+ (в preview було заборонено)
Часті уточнюючі запитання:
- Чи можна додати поле в Record? — Тільки static, instance поля заборонені
- Чи можна додати приватний метод? — Так, приватні методи повністю підтримуються
- Чи можна перевантажити конструктор? — Так, але додатковий конструктор зобов’язаний викликати канонічний
- Чи варто перевизначати equals/hashCode? — Тільки якщо потрібна кастомна логіка (case-insensitive тощо)
Червоні прапорці (НЕ говорити):
- ❌ “В Record не можна додати жодного методу” — Можна static та instance методи
- ❌ “Можна додати mutable поле в Record” — Тільки static поля, instance заборонені
- ❌ “Методи в Record збільшують пам’ять об’єкта” — Методи в class, не в instance
- ❌ “Не можна перевизначити аксесор” — Можна в Java 16+, але з тією ж сигнатурою
Пов’язані теми:
- [[1. Що таке Record в Java і з якої версії вони доступні]]
- [[5. Які методи автоматично генеруються для Record]]
- [[6. Чи можна перевизначити конструктор в Record]]
- [[8. Чи можна оголошувати статичні поля та методи в Record]]