← Назад к вопросам

Можно ли изменить содержимое объекта, хранящегося в final-переменной, с помощью сеттера?

2.3 Middle🔥 141 комментариев
#Docker, Kubernetes и DevOps#ООП

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Да, можно. Final-переменная и состояние объекта — разные вещи

Final-модификатор не делает объект неизменяемым — он блокирует переназначение ссылки. Содержимое объекта остаётся полностью мutable.

Как это работает

Когда ты объявляешь переменную как final, она становится читаемой константой для ссылки, но объект, на который она указывает, может меняться:

public class User {
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;  // Это работает!
    }

    public void setAge(int age) {
        this.age = age;  // И это работает!
    }
}

public class Main {
    public static void main(String[] args) {
        final User user = new User("Alice", 30);
        
        // ✅ Это разрешено — изменяем состояние объекта
        user.setName("Bob");
        user.setAge(31);
        
        // ❌ Это запрещено — пытаемся переназначить ссылку
        // user = new User("Charlie", 25);  // Compilation error!
    }
}

Почему это так

Final-модификатор контролирует ссылку, а не содержимое объекта:

  • final = ссылка всегда указывает на один и тот же объект
  • Содержимое объекта = его поля, которые могут меняться независимо

Пример с immutable объектом

Если ты хочешь настоящей immutability, используй:

  1. Все поля как private final
  2. Инициализация в конструкторе
  3. Без сеттеров
  4. Defensively copy collections
public class ImmutableUser {
    private final String name;
    private final int age;
    private final List<String> emails;

    public ImmutableUser(String name, int age, List<String> emails) {
        this.name = name;
        this.age = age;
        // Defensive copy для Collections
        this.emails = new ArrayList<>(emails);
    }

    public String getName() {
        return name;  // Возвращаем String — immutable
    }

    public int getAge() {
        return age;  // Возвращаем примитив
    }

    public List<String> getEmails() {
        // Defensive copy для защиты от внешних изменений
        return new ArrayList<>(emails);
    }
}

Ключевые различия

КонцепцияЗначение
final поляНе переназначаются после инициализации, но сам объект может меняться
Immutable объектСостояние не может меняться вообще (final поля + без сеттеров)
Thread-safe без синхронизацииЗначит, объект immutable

Практический пример: Mutable vs Immutable

// Mutable — можно менять
final StringBuilder sb = new StringBuilder("Hello");
sb.append(" World");  // ✅ Работает
System.out.println(sb);  // Hello World

// Immutable — нельзя менять
final String str = "Hello";
str = str + " World";  // ❌ Error: Cannot assign to final variable

// Но это новый объект String, не модификация
String result = "Hello".concat(" World");  // ✅ Правильно

Вывод

Final-переменная — это не гарантия неизменяемости. Это гарантия стабильности ссылки. Если тебе нужна настоящая immutability:

  1. Сделай все поля final и private
  2. Убери все сеттеры
  3. Инициализируй данные в конструкторе
  4. Возвращай defensive copies для mutable объектов
  5. Рассмотри использование record (Java 16+) или @Value от Lombok