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

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

У меня – все.

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