Универсальный метод Java для поиска объекта 'max' из коллекции, используя проблему метода CompareTo - PullRequest
2 голосов
/ 15 марта 2019

Предположим, что у нас есть

class A implements Comparable<A>

И

class B extends A

Теперь я хотел бы иметь универсальный метод, который находит самый большой объект из коллекции, используя метод compareTo.

Если я объявлю этот метод следующим образом:

public static <T extends Comparable<T>> T max(List<T> list)

Он не будет работать для аргумента List<B> list, поскольку B не параметризует Comparable с типом B.Чтобы этот метод работал, мне кажется естественным изменить

Comparable<T>

на

Comparable<? super T>

Так что метод после этого изменения будет выглядеть так:

public static <T extends Comparable<? super T>> T max(List<T> list)

Теперь передаваемый аргумент List<B> list работает нормально и не выдает ошибок.Хорошо.Но когда я изменяю сигнатуру метода на

public static <T extends Comparable<T>> T max(List<? extends T> list)

, она также отлично работает для аргумента List<B> list!И мой вопрос почему?Может ли кто-нибудь объяснить мне это?Спасибо:)

Ответы [ 2 ]

1 голос
/ 15 марта 2019

List<? extends T> означает «Список. Мы не знаем Список того, что, но чем бы он ни был, мы знаем, что он расширяет T или сам T».

Одним из эффектов этого являетсячто когда мы возьмем элемент из списка, компилятор будет знать его как тип T. Фактический класс времени выполнения элемента может быть подтипом T, как всегда в случае с полиморфизмом, но это никого не волнует.Мы знаем, что он может храниться в переменной типа T.

Еще один эффект заключается в том, что мы не можем поместить какой-либо элемент в список, потому что мы не знаем, что такое список.Поэтому, что бы мы ни пытались положить внутрь, мы не знаем, является ли это приемлемым типом, поэтому мы не можем.Но при поиске максимума это не имеет значения: мы все равно ничего не добавим.

Следовательно, List<B> также можно принять за List<? extends A>, если необходимо (вдруг решите, что мы неМы не знаем, что это за список, но что бы он ни был, он расширяет A.) Это помогает, потому что тогда он позволяет сопоставить сигнатуру метода, принимая T за A.

0 голосов
/ 15 марта 2019

Это не одно и то же.

В первом примере вы говорите: «Для некоторого типа T, такого, что я могу привести любого T к Comparable супертипа T, затем Я приму список T и верну экземпляр T. "

Когда вы вызываете метод с List<B>, поскольку каждый B является Comparable<A>, поэтому верно сказать: «Я могу привести любой B к Comparable супертипа B ", а параметр типа T разрешается в конкретный тип B.

Во втором примере вы говорите: «Для некоторого типа T, такого, что я могу привести любое T к сопоставимому значению T, тогда я приму любое список T или любой список любого подтипа T и вернуть экземпляр T. "

Когда вы вызываете этот один с A List<B>, тогда параметр типа T разрешается в A; конечно, любой A является Comparable<A>, и вы можете передать List<B> в метод, потому что список B действительно является списком подтипа A.

Так вот почему оба скомпилируют.

Но вы спрашиваете о цели возможности сказать List<? extends T>. Это позволяет вам оставаться открытым для получения коллекций (или других параметризованных типов) подклассов класса, с которым вы работаете. Обычно это то, что вам нужно, когда вы собираетесь получать объекты определенного типа и что-то с ними делать, и, как правило, вы одинаково счастливы сделать это с подтипом (поскольку подтип должен соответствовать одному и тому же контракту).

Ключевое слово super означает, что вы можете работать с объектами, чей параметр типа является суперклассом вашего параметра типа. Типичный случай, когда вы хотите добавить объект в коллекцию. Предположим, что T здесь разрешается в конкретный тип B. Если вы получили List<A> myList, тогда List.add(B) было бы законно, потому что можно добавить B к List<A>. Также полезно, если ваш метод возвращает, скажем, Set<T> - в этом случае вызывающий код не может присвоить его Set<A>, даже если все в наборе является A, но если вы вместо этого вернете Set<? super T> то может. В вашем случае вы собираетесь позвонить Comparable<A>.compare(B) и здесь, аналогично, законно передавать экземпляр B в Comparable<A>.compare(A).

Для этого есть мнемоника, обсуждаемая здесь PECS "Продюсер расширяет, потребительский супер": Что такое PECS (Продюсер расширяет потребительский супер)?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...