Я заметил спецификацию 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 ()?




Джош Блох в этом году выступил на Google I / O с докладом под названием Эффективная перезагрузка Java, который может вас заинтересовать. В нем говорится о мнемонике под названием «Pecs» (производитель extends, потребительский super), которая объясняет, почему вы используете ? extends T и ? super T в своих входных параметрах (только; никогда для возвращаемых типов) и когда их использовать.
Для вас очевидно, что в случае Comparator подойдет любой предок T. Но компилятор не знает, что класс Comparator так работает - ему просто нужно указать, должен ли он разрешать <T> или <? super T>.
С другой стороны, да, это правда, что любой Comparator предка будет работать в этом случае - и способ, которым разработчик библиотеки говорит, что использовать <? super T>.
но для меня это не просто очевидно - Comparator <T> означает, что у него есть некоторые функции, которые работают только с объектами типа T. Поскольку все объекты ClassB также являются объектами ClassA, все функции Comparator <ClassA>, которые работают только на ClassA также будет работать на ClassB. Никаких человеческих знаний не требуется AFAIK
@Kip: Опять же, посмотрите, что Джош Блох говорит, почему дженерики умышленно были спроектированы таким образом, что Foo <Derived> действительно нет расширяется от Foo <Base>, даже если Derived сам расширяется от Base. :-)
Это похоже на 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>; тот факт, что язык требует указания подстановочного знака разработчиком библиотеки, позволяет ему или ей разрешить или ограничить такое использование.
Спасибо, это полезная презентация. Я думаю, что корень моей проблемы - это первый настоящий слайд: List <String> не является подклассом List <Object>, как String [] является подклассом Object [].