JavaRush /Курсы /Java Collections /Generics: super, extends, list

Generics: super, extends, list

Java Collections
5 уровень , 5 лекция
Открыта

— Новая интересная тема — wildcards.

По смыслу, это что-то вроде шаблона «*», который совпадает с чем угодно.

Но давай начнем издалека.

Представь, что у тебя есть класс «Warrior(Воин)» и метод, который вычисляет, какой из двух воинов сильнее. Вот как, например, это могло бы выглядеть:

Пример 1
class WarriorManager
{
 public static boolean fight(Warrior w1, Warrior w2)
 {
  return w1.power > w2.power;
 }
}
Пример вызова
MagicWarrior mag = new MagicWarrior();
ArcherWarrior archer = new ArcherWarrior();

boolean isMagicCooler = WarriorManager.fight(mag, archer);

MagicWarrior и ArcherWarrior – это классы-наследники от Warrior.

Немного простовато, но для примера сойдет.

— Ок.

— И вот допустим, ты решил сделать аналогичный метод, где уже дерутся воины стенка на стенку.

Пример 1
class WarriorManager
{
 public static boolean fight(Warrior w1, Warrior w2)
 {
  return w1.power > w2.power;
 }

 public static boolean fight(List<Warrior> w1, List<Warrior> w2)
 {
  return 
 }
}
Пример вызова
ArrayList<MagicWarrior> magi = new ArrayList<MagicWarrior>();
for(int i=0;i<10;i++)
 magi.add(new MagicWarrior());

ArrayList<ArcherWarrior> archers = new ArrayList<ArcherWarrior>();
for(int i=0;i<10;i++)
 archers.add(new ArcherWarrior());

boolean isMagicCooler = WarriorManager.fight(magi, archers); //ошибка компиляции!

И вот тут ты встречаешь ошибку компиляции и недоумеваешь, а что не так-то?

Все дело в том, что MagicWarrior – это наследник Warrior и его объекты можно передавать в метод fight(Warrior, Warrior)

А вот List<MagicWarrior> — это не наследник List<Warrior>. И передавать его туда нельзя!

— Как это не наследник?

— А вот так. То List и то – List. Пусть и с параметрами.

— Да, а я как-то сразу не обратил внимание. И что, есть уже решение этой проблемы?

— Ага. Для этого используется более сложная конструкция. Выглядит это так:

Пример 1
class WarriorManager
{
 public static boolean fight(Warrior w1, Warrior w2)
 {
  return w1.power > w2.power;
 }

 public static boolean fight(List<? extends Warrior> w1, List<? extends Warrior> w2)
 {
  return …
 }
}

«? extends Warrior» обозначает «любой тип, унаследованный от Warrior ».

Т.е. туда можно передать список List<MagicWarrior> и список List<ArcherWarrior>, и все будет работать.

— Так это же отлично. Чем меньше всяких таких проблем, тем лучше.

— Так и я о том же.

— А если мне не нужен наследник Warrior? Что если я хочу, чтобы в метод можно было передать любой List с любым параметром? Так можно?

— Да, для описания этой ситуации существует две записи:

List<? extends Object>
List<?>

Они эквивалентны по смыслу, поэтому обычно используют вторую.

У меня – все.

— Спасибо, Элли, действительно узнал сегодня много нового.

Комментарии (47)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
safelogj Уровень 51
4 октября 2024
насколько было бы понятней что <?> это про множество типов, а <Т> это про один какой то тип, если б объяснение выглядело так,

«? extends Warrior» обозначает «любЫЕ типЫ, унаследованныЕ от Warrior ».
9194199 Уровень 36 Expert
20 января 2025
Господи, спасибо! Я уже 3-й день пытаюсь понять, в чём между ними разница. Нигде, блин, так и не сказали эту простую и понятную фразу.
o0000q Уровень 38
10 августа 2023
закончилась черная материя.... норма?
very junior java developer Уровень 51
8 октября 2023
странно. наверное задачи пропускал... у меня 3000+ её...
Алексей Барищук Уровень 45 Expert
27 мая 2024
я такая же фигня
Алексей С Уровень 33
8 июля 2023
? это мое состояние мозга 😄
Микола Уровень 51
30 мая 2023
Де про super?
BucketOnHead Уровень 51 Expert
30 марта 2023
Символ '?' ещё называют метасимволом
TemaCode Уровень 51
21 января 2023
чем то отличаются List<? extends Object> и List<Е extends Object> ?
Kurama Уровень 50
1 марта 2023
По тому, что они принимают, нет
milniy87 Уровень 41
28 марта 2023
Для параметров типа коллекций или массивов следует использовать ограничение типа <? extends Number>, а для параметров типа классов или методов, которые принимают один параметр типа, следует использовать ограничение типа <T extends Number>. Это связано с тем, что типы параметров типа коллекций и массивов могут включать множество различных подтипов класса Number, а не только один конкретный подтип. Поэтому ограничение типа <? extends Number> позволяет указать, что тип параметра типа должен быть подтипом класса Number, но не конкретизирует, какой именно подтип. С другой стороны, ограничение типа <T extends Number> гарантирует, что тип параметра типа T является подтипом класса Number и может использоваться в классах или методах, которые принимают один параметр типа. Таким образом, для коллекций следует использовать <? extends Number>, а для типов - <T extends Number>
On1k Уровень 45
6 июля 2022
Интересная отсылка к игре UNO =)
Наиль Гафиятов Уровень 49
20 апреля 2022
Лекция про wildcards, но в итоге даже не написали, что дикая карта это <?> )) А в примере создали метод, принимающий объекты, а передают в него список, ясное дело, что код не будет компилироваться. Амиго неожиданно отупел)
Kurama Уровень 50
1 марта 2023
Так списки - это объекты Сделали бы немного иначе, и со списками было бы всё норм:

// Warrior лучше бы сделать интерфейсом

ArrayList<Warrior> magi = new ArrayList<>();
for(int i=0;i<10;i++)
 magi.add(new MagicWarrior());

ArrayList<Warrior> archers = new ArrayList<>();
for(int i=0;i<10;i++)
 archers.add(new ArcherWarrior());

boolean isMagicCooler = WarriorManager.fight(magi, archers);
Taurnil Уровень 51
8 июля 2023
Вот тоже подумалось. Везде и всюду стараешься минимизировать и унифицировать типизацию, используя обобщения к родительским классам и интерфейсам. Чтобы не думалось, кому же там этот метод должен быть предназначен :) А в данном примере эта концепция искусственно ломается чисто чтобы показать - вот так вот тоже можно. Так тоже можно, да. Только зачем? Когда гораздо удобнее иметь небольшое количество базовых классов и интерфейсов, и типизировать все по ним? Наверное, данный функционал применим только тогда, когда уже есть кусок кода, который менять нельзя, и нужно что-то свое поверх написать...
Darth Nihilus Уровень 35
21 мая 2021
Просто жор материи
25 сентября 2021
это же последние лекции
Артур Жуков Уровень 32
16 февраля 2021
magi
LuneFox Уровень 41 Expert
29 января 2022
Я до конца лекции надеялся хотя бы раз увидеть нормальное mages. Почему ну luchniki тогда?...
Дмитрий Рыбин Уровень 41
1 февраля 2022
magi Википедия Волхвы были жрецами в зороастризме и более ранних религиях западных иранцев. Самое раннее известное использование слова "волхвы" содержится в трехъязычной надписи, написанной Дарием Великим, известной как надпись Бехистун.