Какую нижнюю границу wildcard выбрать, чтобы метод безопасно добавлял Integer в коллекцию Integer или его супертипа?
Используйте параметризацию List<? super Integer>. Она принимает коллекции типа Integer, Number или Object и гарантирует, что в них можно безопасно добавлять значения Integer.
При чтении из такой коллекции гарантированно доступен только тип Object, потому что фактический тип коллекции может быть неизвестен.
Generics появились в Java, чтобы обеспечить проверку типов во время компиляции и уменьшить необходимость в небезопасных приведениях типов. Однако параметризованные типы в Java инвариантны: коллекция подтипа не становится автоматически коллекцией супертипа.
Wildcard позволили описывать ограниченную вариативность. Верхняя граница выражает источник значений, а нижняя — приемник значений определенного типа.
Типы List<Integer>, List<Number> и List<Object> не являются взаимозаменяемыми, несмотря на отношение наследования между их аргументами. Поэтому параметр List<Number> не позволит передать в метод List<Integer> или List<Object>.
Если метод должен добавлять Integer в коллекцию любого подходящего супертипа, использование конкретного параметра типа сделает API слишком узким. Если же разрешить произвольный wildcard без нижней границы, компилятор не сможет гарантировать безопасность добавления.
Запись ? super Integer означает: фактический аргумент — это Integer или некоторый его супертип. Поэтому операция добавления Integer безопасна для любого допустимого варианта.
Извлечение ограничено типом Object: компилятор не знает, является ли фактическая коллекция списком Integer, Number или Object. Это главный компромисс нижней границы: она расширяет множество допустимых аргументов для записи, но уменьшает точность типа при чтении.
В коллекцию List<? super Integer> также можно добавлять значения подтипов Integer, если бы они существовали. Нельзя безопасно добавлять произвольный Number, поскольку фактической коллекцией может оказаться List<Integer>.
Нижняя граница не означает, что коллекция изменяет свой фактический тип. Она лишь описывает безопасный набор операций через ссылку с wildcard; исходная коллекция по-прежнему имеет собственную параметризацию.
Сервис формирует набор числовых идентификаторов и должен добавлять их в коллекцию, принадлежащую вызывающему коду. Вызвавшая сторона может использовать List<Integer> для строгой типизации, List<Number> для дальнейших вычислений или List<Object> для общего контейнера.
Вариант с List<Integer> прост, но исключает коллекции супертипов. Вариант с List<Number> не принимает List<Integer>, а параметр List<?> принимает все варианты, но запрещает добавление конкретного числа.
Выбор List<? super Integer> сохраняет безопасность и поддерживает все три сценария. Результат — один метод без перегрузок и без небезопасных приведений типов; ограничением остается невозможность извлекать элементы как Integer без дополнительной проверки.
1. Почему нельзя объявить такой параметр как List<Number>, если Integer наследуется от Number?
Потому что обобщенные типы в Java инвариантны. Если бы List<Integer> можно было использовать как List<Number>, через такую ссылку можно было бы добавить, например, Double, после чего исходный список содержал бы значение, не являющееся Integer. Инвариантность предотвращает такую ошибку на этапе компиляции.
2. Почему из List<? super Integer> нельзя безопасно получить Integer?
Фактический тип может быть List<Object> или List<Number>. В этих коллекциях уже могут находиться объекты, не являющиеся Integer, поэтому единственный общий безопасный тип чтения — Object. Приведение результата к Integer допустимо только при наличии отдельной проверки и может завершиться исключением.
3. Чем нижняя граница отличается от верхней в направлении разрешенных операций?
List<? extends Integer> описывает неизвестную коллекцию типа Integer или его подтипа: из нее можно читать значения как Integer, но нельзя добавлять произвольный Integer. List<? super Integer> описывает коллекцию типа Integer или его супертипа: в нее можно добавлять Integer, но читать элементы можно только как Object.
Это отражает практическое правило: верхняя граница удобна для источника данных, а нижняя — для приемника данных.