Каким образом Collections.checkedList обнаруживает добавление элемента несовместимого типа при стирании обобщений?
Collections.checkedList сохраняет переданный объект Class и проверяет тип каждого элемента в операциях изменения списка. Поэтому попытка добавить объект несовместимого типа завершается ClassCastException во время выполнения, несмотря на стирание обобщений.
Это не делает исходный список физически типобезопасным: проверяется только доступ через полученное представление. Другой алиас исходного списка по-прежнему может обойти эту защиту.
До широкого применения обобщений Java-код часто использовал необобщённые коллекции, принимающие значения типа Object. После появления generics компилятор начал проверять типы на этапе компиляции, но старые API, raw-типы и взаимодействие с legacy-кодом сохранились.
Проверяемые представления Collections.checkedList, checkedSet и checkedMap решают совместимую задачу: добавить runtime-проверку к уже существующей коллекции без немедленной миграции всего кода и без копирования данных.
Предположим, типизированный код передаёт список в старый метод, принимающий необобщённый List. Такой метод может добавить в список объект любого типа. Ошибка проявится позднее, например при чтении элемента как строки, далеко от места некорректной вставки.
Без защитного представления нарушитель может изменить общую коллекцию через другой алиас. Это приводит к позднему падению, усложняет диагностику и может нарушить предположения вызывающего кода о типах элементов.
При создании checked-представления указывается класс допустимых элементов. Обёртка сохраняет ссылку на исходный список и перед изменением проверяет добавляемый объект через переданный объект Class.
В примере необобщённая ссылка checked компилируется, но фактический вызов проходит через обёртку safe, поэтому число отвергается сразу. Строка добавляется в тот же исходный список: checked-представление не является копией и не меняет структуру хранения.
Проверки применяются к операциям модификации, выполняемым через обёртку, включая добавление отдельных элементов и коллекций, а для списка — соответствующие операции итератора. Ошибка возникает при нарушении объявленного типа, обычно как ClassCastException.
Важное ограничение: уже существующие некорректные элементы не очищаются автоматически. Если изменить исходный список напрямую, в обход checked-представления, защита не сработает; при последующем использовании элемента через типизированный интерфейс ошибка может возникнуть позднее.
Представление сохраняет обычные свойства исходного списка: его сложность операций, порядок, поддержку null и потокобезопасность. Например, checked-list не становится синхронизированным автоматически; при конкурентном доступе нужна отдельная стратегия синхронизации.
Сервис хранит строки в списке, но старый модуль принимает необобщённый List и иногда добавляет туда объекты из внешнего источника. Вариант с полной миграцией старого модуля на generics даёт лучшую проверку на этапе компиляции, но требует значительного объёма изменений и может быть недоступен сразу.
Вариант с копированием в новый List<String> изолирует данные и позволяет дополнительно валидировать содержимое, но требует памяти и времени на копирование, а последующие изменения исходного списка не будут видны. Вариант с checked-представлением не копирует данные и обнаруживает ошибку непосредственно в месте записи, но защищает только операции, выполненные через него.
Для поэтапной модернизации выбран Collections.checkedList как граница между legacy-модулем и новым кодом. Дополнительно исходная ссылка не передаётся вызывающему коду, а долгосрочным решением остаётся перевод API на List<String> и устранение raw-типов.
Защищает ли checkedList все ссылки на исходный список?
Нет. Это не копия и не изменение типа самого объекта, а только проверяемое представление. Если сохранить исходный список или другой алиас и изменить его напрямую, вставка неподходящего объекта может пройти без проверки.
Проверяет ли checkedList уже находящиеся в списке элементы?
Нет. При создании обёртки содержимое не просматривается и не преобразуется. Поэтому некорректные элементы, добавленные ранее через raw-ссылку, могут остаться; checked-представление предотвращает новые нарушения через свои операции, но не исправляет прошлые.
Заменяет ли checkedList обобщения и проверку компилятора?
Нет. Generics предпочтительнее, потому что выявляют ошибки статически и не требуют затрат runtime-проверок. Checked-представление — защитный механизм для границ с legacy-кодом, raw-типами или неконтролируемыми вызывающими сторонами; после устранения таких границ его обычно можно убрать.