"Ahora, un tema nuevo e interesante: los comodines".

"Esencialmente, esto es algo así como un patrón «*» que coincide con cualquier cosa".

"Pero empecemos desde el principio".

"Imagina que tienes una clase de Guerrero y un método que determina cuál de los dos guerreros es más fuerte. Así es como se vería esto:"

Ejemplo 1
class WarriorManager
{
 public static boolean fight(Warrior w1, Warrior w2)
 {
  return w1.power > w2.power;
 }
}
Llamada de método de ejemplo
MagicWarrior mag = new MagicWarrior();
ArcherWarrior archer = new ArcherWarrior();

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

"MagicWarrior y ArcherWarrior heredan Warrior. ."

"Es un poco simplista, pero como ejemplo, servirá".

"DE ACUERDO."

"Ahora supongamos que ha decidido hacer un método similar para la situación en la que varios guerreros se han unido a la refriega".

Ejemplo 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}
}
Llamada de método de ejemplo
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); // Compilation error!

"Pero aquí te encuentras con un error de compilación y no sabes qué podría estar mal".

"La cuestión es que MagicWarrior hereda Warrior , y los objetos MagicWarrior se pueden pasar al método fight(Warrior, Warrior)".

"Pero List<MagicWarior> no hereda List<Warrior> . ¡Entonces, no puedes pasarlo allí!"

"¿Qué quieres decir con que no lo hereda?"

"Quiero decir esto: uno es una Lista y el otro es una Lista, pero tienen parámetros de tipo".

"Tienes razón. De alguna manera no me di cuenta de eso de inmediato. Entonces, ¿ya hay una solución para este problema?"

"Sí. Necesitas usar una estructura más compleja. Así es como se ve:"

Ejemplo 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}
}

"La parte «? extiende a Warrior» significa «cualquier tipo que herede a Warrior»"

"En otras palabras, ahora puedes pasar una List<MagicWarrior> y una List<ArcherWarrior>, y todo funcionará".

"Eso es increíble. Cuantos menos problemas, mejor".

"Así es como me siento yo también".

"Pero, ¿y si no necesito algo que herede Warrior? ¿Qué pasa si quiero poder pasar cualquier lista con cualquier tipo de parámetro al método? ¿Está permitido?"

"Sí, hay dos formas de notación para hacer eso:"

List<? extends Object>
List<?>

"Ambos significan lo mismo, por lo que generalmente se usa la segunda versión".

"Eso es todo por hoy."

"Gracias, Ellie. Realmente aprendí mucho hoy".