Как объясняется запрет проверки параметризованного типа через instanceof в Java?
Проверка через instanceof требует типа, информацию о котором можно определить во время выполнения. Параметр типа, например String в List<String>, после компиляции обычно стирается, поэтому Java разрешает проверять List<?>, но запрещает проверять List<String>.
Обобщения в Java создавались как средство статической типобезопасности без обязательного изменения существующей объектной модели и большого объёма ранее написанного кода. Для этого применяется стирание типов: параметры обобщений в основном используются компилятором, а не хранятся в обычном runtime-представлении объекта.
Такой подход упростил совместимость обобщённого кода с неуниверсальным кодом, но привёл к ограничению: во время выполнения нельзя надёжно различить экземпляры List<String> и List<Integer>.
Представим объект списка, фактический тип которого — ArrayList<String>. После стирания типов JVM видит объект списка, но не получает из его обычного runtime-типа сведения о том, что элементами должны быть именно строки.
Если разрешить проверку instanceof List<String>, результат мог бы создавать ложное ощущение, что JVM проверила тип элементов. На самом деле она смогла бы проверить только сам List, поэтому такая конструкция запрещена на этапе компиляции.
instanceof работает с reifiable type — типом, чья необходимая информация доступна во время выполнения. К таким типам относятся обычные классы, необобщённые типы и безопасно неизвестный параметризованный тип List<?>.
List<String> не является reifiable type: после стирания параметр String не участвует в обычной проверке типа. Поэтому проверка параметризованного типа запрещена, тогда как проверка List<?> допустима: она требует установить только факт реализации List, не утверждая конкретный тип элементов.
В этом примере проверяется только принадлежность объекта к List. Тип элементов можно уточнять безопасно лишь через операции, которые учитывают фактические данные, либо передавать явный объект типа, например Class<String>, если проверка конкретного класса действительно необходима.
Нельзя обойти ограничение обычным приведением к List<String> без риска: такое приведение проверяет только сырой тип List, а ошибка может проявиться позже при извлечении элемента. Подавление предупреждения устраняет сообщение компилятора, но не добавляет runtime-проверку параметра типа.
Сервис принимает объект типа Object и должен понять, является ли он списком строк. Вариант с проверкой instanceof List<String> не компилируется. Проверка instanceof List<?> допустима, но подтверждает только тип контейнера, поэтому считать каждый элемент строкой без дополнительной валидации нельзя.
Можно проверить элементы вручную после получения List<?>: это даёт корректный результат, но требует обхода списка и имеет стоимость по времени. Другой вариант — принять вместе с данными Class<String> и проверять элементы через этот объект; решение явнее, но усложняет API. Практически выбирают проверку List<?> с последующей валидацией элементов, если вход внешнего происхождения, и документированный контракт с приведением — только если источник уже гарантирует тип.
Почему проверка List<?> разрешена, если это тоже параметризованный тип?
List<?> не утверждает конкретный тип элементов. Он означает «список элементов неизвестного типа», поэтому для проверки достаточно установить, что объект реализует List. Такой тип является reifiable и безопасен для instanceof.
Можно ли после проверки instanceof List<?> добавить список строк в этот список?
Нет, произвольную строку добавлять нельзя: неизвестно, какой конкретный тип элементов требуется фактическому списку. Из List<?> безопасно читать значения как Object, но запись возможна только значения null, поскольку null совместим с любым ссылочным типом.
Почему приведение к List<String> иногда компилируется, хотя проверка instanceof List<String> запрещена?
Приведение может компилироваться с предупреждением о непроверяемом приведении. JVM проверяет только доступную часть типа — List — а параметр String не проверяется из-за стирания. Поэтому такое приведение не доказывает тип элементов и требует доверия к контракту или отдельной проверки содержимого.