В отношении использования дженериков Java с нижними границами:? супер Т
Я пытаюсь понять использование подстановочных знаков с нижней границей в некоторой глубине. Я пытаюсь написать общий метод copy
, который копирует содержимое одного List
в другое. Я придумал эту подпись метода:
<T> void copy(List<T> dest, List<? extends T> src)
Я думаю, что эта подпись является всеобъемлющей для решения всех сценариев. Однако я вижу, что в классе Java Collections подпись метода выглядит так:
<T> void copy(List<? super T> dest, List<? extends T> src)
Я не понимаю, почему они используют List<? super T> dest
вместо просто List<T> dest
. Есть ли дополнительная гибкость с их подписью?
Ответы
Ответ 1
Вот пример:
Следующий фрагмент передает компиляцию с сигнатурой <T> void copy(List<? super T> dest, List<? extends T> src)
, но не работает с сигнатурой <T> void copy(List<T> dest, List<? extends T> src)
:
YourClass obj = new YourClass ();
List<HashMap<String,String>> lhm = new ArrayList<>();
List<Map<String,String>> lm = new ArrayList<>();
obj.<HashMap<String,String>>copy (lm,lhm);
Ответ 2
Нет никаких практических различий без очевидных свидетелей типов.
Без указания свидетельства типа, сделанного Эраном, между этими двумя методами нет различий в гибкости.
По сути, использование ? super T
over T
является только стилистической разницей, однако это лучше практика, как видно из применения ряда принципов хорошего кода:
- Явное намерение:
? super T
более явно показывает, какие типы dest
должны принимать.
- Модульность: вам вообще не нужно смотреть на ограничения типа
src
, чтобы знать, какие типы dest
могут принимать.
- Producer Extends, Consumer Super (PECS): параметр производителя (далее "in" ) должен использовать
extends
, а потребительский параметр ( "out" ниже) следует использовать ключевое слово super
.
Использование ? super T
также рекомендуется с помощью учебных пособий Java (они даже используют функцию copy
):
Для целей этого обсуждения полезно рассматривать переменные как одну из двух функций:
Переменная "В"
Переменная "in" служит для передачи данных в код. Представьте себе метод copy
с двумя аргументами: copy(src, dest)
. Аргумент src
предоставляет данные для копирования, поэтому он является параметром "in".
Переменная "Out"
Переменная "out" содержит данные для использования в другом месте. В примере copy
copy(src, dest)
аргумент dest
принимает данные, поэтому он является параметром "out".
Вы можете использовать принцип "in" и "out" при принятии решения о том, использовать ли подстановочный знак и какой тип подстановочных знаков подходит. В следующем списке приведены следующие рекомендации:
Подстановочные знаки:
- Переменная "in" определяется с помощью верхнего ограниченного шаблона, используя
extends
. - Переменная "out" определяется с нижним ограниченным wildcard, используя ключевое слово
super
.