Чи є поля Record фінальними
Це робить Record immutable (незмінюваним) за дизайном.
🟢 Junior Level
Так, всі поля в Record автоматично є final. Це означає, що їх не можна змінити після створення об’єкта.
public record User(String name, int age) {}
User user = new User("John", 25);
// ❌ Не можна змінити поля
user.name = "Jane"; // compilation error — поле final
// ✅ Можна створити новий Record
user = new User("Jane", 25); // OK
Це робить Record immutable (незмінюваним) за дизайном.
🟡 Middle Level
Як це працює
Компілятор автоматично робить всі поля private final:
public record Point(int x, int y) {}
// Компілятор генерує:
public final class Point extends java.lang.Record {
private final int x; // implicit final
private final int y; // implicit final
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int x() { return x; }
public int y() { return y; }
}
Навіть якщо ви не написали final:
// Обидва варіанти однакові
public record Point(int x, int y) {}
public record Point(final int x, final int y) {} // final надмірний
Важливий нюанс: mutable компоненти
Поля final, але якщо вони посилаються на mutable об’єкти, то вміст можна змінити:
public record User(String name, List<String> tags) {}
User user = new User("John", new ArrayList<>(List.of("admin", "user")));
// ❌ Не можна замінити список
// user.tags = new ArrayList<>(); // compilation error
// ✅ Але можна змінити вміст списку
user.tags().add("moderator"); // OK! Список mutable
user.tags().clear(); // OK!
Рішення — defensive copy:
public record User(String name, List<String> tags) {
public User {
tags = List.copyOf(tags); // immutable список
}
}
User user = new User("John", List.of("admin"));
// user.tags().add("moderator"); // UnsupportedOperationException!
Типові помилки
- Очікування повної іммутабельності: ```java public record Data(int[] values) {}
Data d = new Data(new int[]{1, 2, 3}); d.values()[0] = 99; // ✅ Можна — масив mutable!
// ✅ Рішення
public record SafeData(List
2. **Спроба змінити поле:**
```java
public record Point(int x, int y) {
public void setX(int x) {
// this.x = x; // compilation error — final поле
}
}
🔴 Senior Level
Internal Implementation
JVM level:
// Всі компоненти Record мають ACC_FINAL прапорець
Field {
access_flags: ACC_PRIVATE | ACC_FINAL
name: "x"
descriptor: "I"
}
Reflection підтверджує:
Field[] fields = Point.class.getDeclaredFields();
for (Field f : fields) {
System.out.println(f.getName() + " final=" + Modifier.isFinal(f.getModifiers()));
}
// Вивід: x final=true, y final=true
Архітектурні Trade-offs
Final поля:
| Плюси | Мінуси |
|---|---|
| Thread-safe (safe publication) | Не можна змінити стан |
| JIT оптимізації | Потрібно створювати нові об’єкти |
| Передбачуваність | Mutable компоненти вимагають захисту |
| HashMap ключі | Складше працювати з графами |
Edge Cases
1. Mutable компоненти:
public record MutableRecord(Date createdAt) {}
MutableRecord r = new MutableRecord(new Date());
r.createdAt().setTime(0); // можна змінити Date!
// ✅ Рішення
public record ImmutableRecord(Date createdAt) {
public ImmutableRecord {
createdAt = new Date(createdAt.getTime()); // defensive copy
}
}
2. Final ≠ immutable:
public record Config(Map<String, String> props) {}
// final поле, але Map можна змінити
Config c = new Config(new HashMap<>());
c.props().put("key", "value"); // OK
3. Memory visibility:
// final поля гарантують safe publication
// після завершення конструктора всі потоки бачать коректні значення
public record SharedConfig(String url, int timeout) {}
// Publication safe
SharedConfig config = new SharedConfig("http://api.com", 5000);
// Інший потік побачить коректні значення
Продуктивність
Final поля:
- JIT може інлайнити значення
- Немає необхідності в volatile
- Safe publication без синхронізації
- Memory barrier при конструюванні
Бенчмарк:
// Приблизні значення. Залежать від JVM/hardware.
Операція | Final поля | Mutable поля
-------------------|------------|-------------
Read | 1 ns | 1 ns
Publish (thread) | 0 ns | sync needed
JIT optimization | Максимальна | Обмежена
Production Experience
Immutable DTO:
public record OrderDto(
String id,
Instant createdAt,
BigDecimal amount,
List<OrderItem> items
) {
public OrderDto {
items = List.copyOf(items); // захист mutable колекції
}
}
Value objects:
public record Money(BigDecimal amount, Currency currency) {
// Обидва поля final — повністю immutable
public Money add(Money other) {
// Повертаємо новий об'єкт, не змінюємо поточний
return new Money(this.amount.add(other.amount), this.currency);
}
}
Best Practices
// ✅ Record для immutable даних
public record User(String name, Email email) {}
// ✅ Defensive copy для mutable компонентів
public record Tags(List<String> values) {
public Tags { values = List.copyOf(values); }
}
// ✅ Immutable колекції
public record Config(Map<String, String> props) {
public Config { props = Map.copyOf(props); }
}
// ❌ Mutable компоненти без захисту
public record BadRecord(Date date, int[] array) {}
// ❌ Очікування що final = повністю immutable
🎯 Шпаргалка для співбесіди
Обов’язково знати:
- Всі поля Record автоматично
private final— компілятор додає final - Final означає не можна замінити посилання, але mutable об’єкти всередині можна змінити
- Масиви, Date, колекції — mutable компоненти, вимагають defensive copy
List.copyOf(),Set.copyOf(),Map.copyOf()для захисту mutable колекцій- Final поля гарантують safe publication в багатопоточному середовищі (без volatile)
- JIT може краще оптимізувати final поля (inlining)
Часті уточнюючі запитання:
- Чи можна змінити масив всередині Record? — Так, масив mutable:
record.values()[0] = 99 - Як захистити mutable компонент? — Defensive copy в компактному конструкторі:
tags = List.copyOf(tags) - Final = повністю immutable? — Ні, final захищає тільки посилання, не вміст об’єкта
- Чому final важливий для HashMap ключів? — Змінений ключ = зламаний hashCode, дані втрачені
Червоні прапорці (НЕ говорити):
- ❌ “Final означає повну іммутабельність” — Mutable компоненти можна змінити
- ❌ “Масив в Record безпечний” — Масиви завжди mutable, потрібен defensive copy
- ❌ “Можна змінити поле через setter” — Поля final, setter неможливий
- ❌ “Final не дає переваг” — Final дає safe publication та JIT оптимізації
Пов’язані теми:
- [[1. Що таке Record в Java і з якої версії вони доступні]]
- [[5. Які методи автоматично генеруються для Record]]
- [[7. Що таке компактний конструктор (compact constructor) в Record]]
- [[10. Чи можна використовувати Record як ключ в HashMap]]