При чтении из коллекции с параметризацией «неизвестный супертип Integer» какой статический тип получает элемент и почему?
При чтении из коллекции типа ? super Integer элемент имеет статический тип Object. Компилятор не знает, является ли фактический тип коллекции Integer, Number или Object, поэтому гарантированно безопасным типом чтения остаётся только Object.
Generics появились в Java 5, чтобы добавить проверку типов на этапе компиляции и уменьшить необходимость в небезопасных приведениях. При этом Java сохранила совместимость с существующим кодом через стирание типов, поэтому обобщения проектировались как средство статической проверки, а не как полноценная информация о типах во время выполнения.
Wildcards понадобились для описания API, работающих с семействами связанных параметризаций. Нижняя граница позволяет принимать коллекцию Integer или любого его супертипа, но неизвестность конкретного типа ограничивает безопасное чтение.
Рассмотрим API, который принимает коллекцию, способную хранить Integer. Фактическим аргументом может быть коллекция Integer, Number или Object.
Если метод прочитает элемент, он не может считать его Integer: коллекция List<Object> формально также подходит, а в ней может находиться любой объект. Неверное предположение привело бы к ошибке приведения типа во время выполнения.
Для ? super Integer компилятор использует захваченный неизвестный тип, который гарантированно является супертипом Integer. Это можно представить как некоторый тип CAP, для которого выполняется условие: Integer является его подтипом.
Фактический тип может быть разным:
List<Integer>;List<Number>;List<Object>.Общий безопасный тип результата чтения для всех этих вариантов — Object. Поэтому значение можно присвоить переменной Object, но нельзя напрямую присвоить переменной Integer.
При этом нижняя граница гарантирует обратное свойство: в такую коллекцию можно безопасно добавить Integer, потому что любая разрешённая коллекция умеет хранить Integer. Но извлечённое значение не считается Integer, поскольку оно могло быть добавлено в коллекцию через другой тип или уже находиться там как объект более общего типа.
Важное следствие: ? super Integer — это не «коллекция, содержащая только Integer», а «коллекция некоторого неизвестного типа, способного принимать Integer». Приведение прочитанного Object к Integer допустимо синтаксически, но его безопасность уже не следует из wildcard и должна обеспечиваться отдельно.
Пусть библиотечный метод передаёт вычисленные целые значения в коллекцию, принадлежащую вызывающему коду. Приём параметра как List<Integer> излишне ограничил бы API: вызывающий не смог бы передать List<Number> или List<Object>.
Вариант List<?> был бы слишком слабым: в него нельзя безопасно добавлять произвольный Integer. Обобщённый параметр с требованием «любой супертип Integer» в синтаксисе Java выразить нельзя.
Поэтому выбирают List<? super Integer>. Метод получает широкую совместимость и может добавлять Integer, но если ему нужно анализировать уже существующие элементы как Integer, такой API не подходит: чтение даёт только Object. В этом случае чтение и запись следует разделить на отдельные методы с более точными контрактами или явно проверять тип элементов.
? super Integer переменной типа Number?Нет, напрямую нельзя. Даже если Integer является подтипом Number, фактическая коллекция может быть List<Object>, а элемент такой коллекции не обязан быть Number.
Гарантированный тип чтения — только Object. Присваивание Number потребовало бы дополнительной проверки типа или приведения, ответственность за которое уже ложится на код разработчика.
Integer безопасно, если чтение не считается безопасным как Integer?Потому что направление гарантии различается. Любой допустимый контейнер — List<Integer>, List<Number> или List<Object> — способен принять значение Integer.
Но при чтении неизвестно, какие значения уже находятся в контейнере. Например, List<Object> может содержать строку или другой объект. Поэтому запись ограниченного типа безопасна, а чтение как этого типа — нет.
List<? super Integer> отличается от List<Object> в параметре метода?List<Object> требует именно параметризацию Object; List<Integer> и List<Number> ему не подходят из-за инвариантности generics. List<? super Integer> описывает всё семейство коллекций, параметризованных Integer или его супертипом.
Следовательно, wildcard делает API более гибким, сохраняя гарантию возможности добавить Integer. Компромисс — потеря точного типа при чтении: метод видит элементы как Object, а не как Integer или Number.