В отношении использования дженериков 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.