Generics: wildcards

Модуль 2. Java Core
5 уровень , 1 лекция
Открыта

— Новая интересная тема — 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<?>

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

У меня – все.

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

Комментарии (10)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Родион Уровень 32
30 мая 2025
тот, кто читает этот комментарий, знай, что если тебе кажется, что примеры непонятные и в них используются приемы которых тебе еще не объясняли, то тебе не кажется
Виктор Ш Уровень 72
17 марта 2025
Примеры понял с трудом. Пойду чат допрашивать
Вячеслав Уровень 40
27 декабря 2024
На мой взгляд, если примеры описывались бы полностью, было бы понятнее
Ольга Уровень 24 Expert
24 апреля 2022
Я так и не поняла, кто сильней МАГ или Лучник?)))
Андрей Федоров Уровень 108 Expert
20 июня 2022
что вкуснее: "мёд или малина?"
Greg Уровень 108 Expert
5 октября 2022
Земляника.
jvatechs Уровень 111 Expert
12 февраля 2023
Однозначно, что маг.
Павел Уровень 46
18 января 2024
возможность создать фаерболл круче возможности пустить стрелу
Рубен Уровень 80
10 мая 2024
тот у кого дробовик
Владимир (koloncovvova@mail.ru) Уровень 108 Expert
11 апреля 2022
как-то мудрено все конечно описано..и примеры запутанные, для новичка все таки сложновато:)