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

Почему возникнет exception при попытке изменить unmodifiable collection?

1.2 Junior🔥 61 комментариев
#Коллекции

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

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

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

Исключение при изменении Unmodifiable Collection

Это хороший вопрос, который показывает глубокое понимание архитектуры коллекций в Java.

Что такое Unmodifiable Collection

Unmodifiable collection - это оболочка (wrapper) над обычной коллекцией, которая запрещает любые операции модификации (add, remove, clear, set).

List<String> original = new ArrayList<>();
original.add("Java");

// Создание unmodifiable view
List<String> unmodifiable = Collections.unmodifiableList(original);

// Попытка модификации вызовет исключение
unmodifiable.add("Python");  // UnsupportedOperationException!

Как это реализовано

Unmodifiable collection - это не специальный тип коллекции, а декоратор (wrapper pattern).

Spring создает внутренний класс, который оборачивает реальную коллекцию:

// Упрощённая реализация java.util.Collections$UnmodifiableList
private static class UnmodifiableList<E> extends AbstractList<E> {
    private final List<? extends E> list;
    
    UnmodifiableList(List<? extends E> list) {
        this.list = list;
    }
    
    // Методы чтения работают
    @Override
    public E get(int index) {
        return list.get(index);
    }
    
    @Override
    public int size() {
        return list.size();
    }
    
    // Методы записи выбрасывают исключение
    @Override
    public E set(int index, E element) {
        throw new UnsupportedOperationException();
    }
    
    @Override
    public void add(int index, E element) {
        throw new UnsupportedOperationException();
    }
    
    @Override
    public E remove(int index) {
        throw new UnsupportedOperationException();
    }
    
    @Override
    public boolean add(E e) {
        throw new UnsupportedOperationException();
    }
    
    @Override
    public boolean remove(Object o) {
        throw new UnsupportedOperationException();
    }
    
    @Override
    public void clear() {
        throw new UnsupportedOperationException();
    }
}

Какое исключение выбрасывается

UnsupportedOperationException - это unchecked исключение, которое выбрасывается при попытке вызвать неподдерживаемую операцию.

List<String> unmodifiable = Collections.unmodifiableList(
    Arrays.asList("Java", "Kotlin")
);

try {
    unmodifiable.add("Python");
} catch (UnsupportedOperationException e) {
    System.out.println("Нельзя модифицировать: " + e.getMessage());
}

// Output: Нельзя модифицировать: null

Разница между Immutable и Unmodifiable

Это важное различие:

// UNMODIFIABLE - только view, оригинал можно изменить
List<String> original = new ArrayList<>();
original.add("Java");

List<String> unmodifiable = Collections.unmodifiableList(original);

// Изменяем оригинал
original.add("Python");

// В unmodifiable это отразится!
System.out.println(unmodifiable.size());  // 2!
unmodifiable.forEach(System.out::println);
// Java
// Python

// Но изменить через unmodifiable нельзя
unmodifiable.add("Ruby");  // UnsupportedOperationException!

Истинная Immutable коллекция (Java 9+)

// Java 9+ - List.of создает ИСТИННО immutable коллекцию
List<String> immutable = List.of("Java", "Kotlin");

// Нельзя изменить
immutable.add("Python");  // UnsupportedOperationException

// Даже оригинальные данные нельзя передать
// List.of создает новый список, не связанный с исходными данными

Практические примеры

1. Защита метода от модификации

public class User {
    private List<String> roles = new ArrayList<>();
    
    // НЕПРАВИЛЬНО - может быть модифицирован
    public List<String> getRoles1() {
        return roles;
    }
    
    // ПРАВИЛЬНО - защищено unmodifiable
    public List<String> getRoles2() {
        return Collections.unmodifiableList(roles);
    }
    
    // ЕЩЕ ЛУЧШЕ - Java 9+
    public List<String> getRoles3() {
        return List.copyOf(roles);  // immutable copy
    }
}

// Использование
User user = new User();
user.getRoles2().add("ADMIN");  // UnsupportedOperationException!

2. Collections.unmodifiableMap

Map<String, Integer> original = new HashMap<>();
original.put("Java", 25);
original.put("Python", 15);

Map<String, Integer> unmodifiable = Collections.unmodifiableMap(original);

// Попытка модификации
unmodifiable.put("Kotlin", 10);  // UnsupportedOperationException
unmodifiable.remove("Java");     // UnsupportedOperationException

3. Collections.unmodifiableSet

Set<String> original = new HashSet<>();
original.add("item1");
original.add("item2");

Set<String> unmodifiable = Collections.unmodifiableSet(original);

for (String item : unmodifiable) {
    System.out.println(item);  // Это работает
}

unmodifiable.add("item3");  // UnsupportedOperationException

Почему это нужно в архитектуре

  1. Инкапсуляция - класс контролирует доступ к своему состоянию
public class Configuration {
    private final List<String> allowedHosts = new ArrayList<>();
    
    public List<String> getAllowedHosts() {
        // Клиент не может случайно изменить конфиг
        return Collections.unmodifiableList(allowedHosts);
    }
}
  1. Предотвращение ошибок - раньше ошибка будет выявлена
// Если вызывающий код пытается модифицировать, он сразу узнает
// что это запрещено, вместо того чтобы потом искать баг
List<String> config = service.getConfiguration();
config.add("something");  // Сразу UnsupportedOperationException
  1. Thread safety - если несколько потоков читают, это безопасно
public class SharedData {
    private List<String> data = Collections.unmodifiableList(
        new ArrayList<>()
    );
    
    // Несколько потоков могут безопасно читать
    // Никто не может модифицировать
}

Вывод

UnsupportedOperationException выбрасывается потому что:

  1. Unmodifiable collection - это декоратор, который явно переопределяет методы модификации
  2. Каждый метод изменения (add, remove, set, clear) проверяет, является ли коллекция modifiable
  3. Если попытка изменения, выбрасывается UnsupportedOperationException
  4. Это дизайн-патерн, который предотвращает случайные модификации защищённых данных

Это хорошая практика использовать unmodifiable коллекции при возврате данных из методов, если нет необходимости в модификации со стороны клиента.