Являются ли поля 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]]