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

Generics: super, extends, list

Открыта

— Новая интересная тема — 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 Android Developer
4 октября 2024, 19:04
насколько было бы понятней что <?> это про множество типов, а <Т> это про один какой то тип, если б объяснение выглядело так,
«? extends Warrior» обозначает «любЫЕ типЫ, унаследованныЕ от Warrior ».
9194199
Уровень 36
Expert
20 января 2025, 13:07
Господи, спасибо! Я уже 3-й день пытаюсь понять, в чём между ними разница. Нигде, блин, так и не сказали эту простую и понятную фразу.
o0000q
Уровень 38
10 августа 2023, 11:59
закончилась черная материя.... норма?
8 октября 2023, 19:14
странно. наверное задачи пропускал... у меня 3000+ её...
Алексей Барищук
Уровень 45
Expert
27 мая 2024, 09:19
я такая же фигня
Алексей С
Уровень 33
8 июля 2023, 11:11
? это мое состояние мозга 😄
Микола
Уровень 51
30 мая 2023, 15:37
Де про super?
BucketOnHeadJava Developer в Уже нигдеExpert
30 марта 2023, 16:55
Символ '?' ещё называют метасимволом
TemaCode
Уровень 51
21 января 2023, 10:59
чем то отличаются List<? extends Object> и List<Е extends Object> ?
Kurama
Уровень 50
1 марта 2023, 15:40
По тому, что они принимают, нет
milniy87
Уровень 41
28 марта 2023, 08:05
Для параметров типа коллекций или массивов следует использовать ограничение типа <? extends Number>, а для параметров типа классов или методов, которые принимают один параметр типа, следует использовать ограничение типа <T extends Number>. Это связано с тем, что типы параметров типа коллекций и массивов могут включать множество различных подтипов класса Number, а не только один конкретный подтип. Поэтому ограничение типа <? extends Number> позволяет указать, что тип параметра типа должен быть подтипом класса Number, но не конкретизирует, какой именно подтип. С другой стороны, ограничение типа <T extends Number> гарантирует, что тип параметра типа T является подтипом класса Number и может использоваться в классах или методах, которые принимают один параметр типа. Таким образом, для коллекций следует использовать <? extends Number>, а для типов - <T extends Number>
On1k
Уровень 2
6 июля 2022, 19:51
Интересная отсылка к игре UNO =)
Наиль Гафиятов Enterprise Java Developer
20 апреля 2022, 10:27
Лекция про wildcards, но в итоге даже не написали, что дикая карта это <?> )) А в примере создали метод, принимающий объекты, а передают в него список, ясное дело, что код не будет компилироваться. Амиго неожиданно отупел)
Kurama
Уровень 50
1 марта 2023, 15:45
Так списки - это объекты Сделали бы немного иначе, и со списками было бы всё норм:
// 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);
TaurnilTechnical Lead в ООО "Теком"
8 июля 2023, 14:10
Вот тоже подумалось. Везде и всюду стараешься минимизировать и унифицировать типизацию, используя обобщения к родительским классам и интерфейсам. Чтобы не думалось, кому же там этот метод должен быть предназначен :) А в данном примере эта концепция искусственно ломается чисто чтобы показать - вот так вот тоже можно. Так тоже можно, да. Только зачем? Когда гораздо удобнее иметь небольшое количество базовых классов и интерфейсов, и типизировать все по ним? Наверное, данный функционал применим только тогда, когда уже есть кусок кода, который менять нельзя, и нужно что-то свое поверх написать...
Darth NihilusРазработчик спокойствия в Rage&Flame Industrie
21 мая 2021, 13:41
Просто жор материи
25 сентября 2021, 08:05
это же последние лекции
Артур Жуков
Уровень 32
16 февраля 2021, 10:13
magi
LuneFoxJava Developer в BIFITExpert
29 января 2022, 17:05
Я до конца лекции надеялся хотя бы раз увидеть нормальное mages. Почему ну luchniki тогда?...
1 февраля 2022, 22:05
magi Википедия Волхвы были жрецами в зороастризме и более ранних религиях западных иранцев. Самое раннее известное использование слова "волхвы" содержится в трехъязычной надписи, написанной Дарием Великим, известной как надпись Бехистун.