Почему SomeClass <? super T> не эквивалентен SomeClass <T> в универсальных типах Java?

Я заметил спецификацию Collections.sort:

public static <T> void sort(List<T> list, Comparator<? super T> c)

Зачем здесь нужен "? super"? Если ClassB расширяет ClassA, то разве у нас не будет гарантии, что Comparator<ClassA> в любом случае сможет сравнивать два объекта ClassB без части «? super»?

Другими словами, с учетом этого кода:

List<ClassB> list = . . . ;
Comparator<ClassA> comp = . . . ;
Collections.sort(list, comp);

почему компилятор недостаточно умен, чтобы знать, что это нормально, даже без указания "? super" для объявления Collections.sort ()?

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
8
0
4 739
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Ответ принят как подходящий

Джош Блох в этом году выступил на Google I / O с докладом под названием Эффективная перезагрузка Java, который может вас заинтересовать. В нем говорится о мнемонике под названием «Pecs» (производитель extends, потребительский super), которая объясняет, почему вы используете ? extends T и ? super T в своих входных параметрах (только; никогда для возвращаемых типов) и когда их использовать.

Спасибо, это полезная презентация. Я думаю, что корень моей проблемы - это первый настоящий слайд: List <String> не является подклассом List <Object>, как String [] является подклассом Object [].

Kip 30.10.2008 22:42

Для вас очевидно, что в случае Comparator подойдет любой предок T. Но компилятор не знает, что класс Comparator так работает - ему просто нужно указать, должен ли он разрешать <T> или <? super T>.

С другой стороны, да, это правда, что любой Comparator предка будет работать в этом случае - и способ, которым разработчик библиотеки говорит, что использовать <? super T>.

но для меня это не просто очевидно - Comparator <T> означает, что у него есть некоторые функции, которые работают только с объектами типа T. Поскольку все объекты ClassB также являются объектами ClassA, все функции Comparator <ClassA>, которые работают только на ClassA также будет работать на ClassB. Никаких человеческих знаний не требуется AFAIK

Kip 30.10.2008 22:13

@Kip: Опять же, посмотрите, что Джош Блох говорит, почему дженерики умышленно были спроектированы таким образом, что Foo <Derived> действительно нет расширяется от Foo <Base>, даже если Derived сам расширяется от Base. :-)

Chris Jester-Young 30.10.2008 22:18

Это похоже на C#, я только что узнал об этом пару дней назад, почему (жесткий путь, а затем информативный способ PDC).

Предположим, Dog extends Animal

Blah<Dog> - это нет, то же самое, что и Blah<Animal>, они имеют совершенно разные сигнатуры типов, хотя Dog расширяет Animal.

Например, предположим метод на Blah<T>:

T Clone();  

В Blah<Dog> это Dog Clone();, а в Blah<Animal> - это Animal Clone();.

Вам нужен способ отличить, что компилятор может сказать, что Blah<Dog> имеет тот же открытый интерфейс, что и Blah<Animal>, и это то, что указывает <? super T> - любой класс, используемый как T, может быть сокращен до его суперкласса с точки зрения Blah<? super T>.

(Я полагаю, что в C# 4.0 это будет Blah<out T>.)

Есть действительно хорошее (но извилистое) объяснение этого в Больше удовольствия с подстановочными знаками.

Простой ответ на ваш вопрос состоит в том, что разработчик библиотеки хотел предоставить пользователю максимальную гибкость; эта подпись метода, например, позволяет вам делать что-то вроде этого:

List<Integer> ints = Arrays.asList(1,2,3);
Comparator<Number> numberComparator = ...;

Collections.sort(ints, numberComparator);

Использование подстановочного знака предотвращает принудительное использование Comparator<Integer>; тот факт, что язык требует указания подстановочного знака разработчиком библиотеки, позволяет ему или ей разрешить или ограничить такое использование.

Другие вопросы по теме