"Ora per un nuovo argomento interessante: i caratteri jolly."

"Essenzialmente, questo è qualcosa come un modello «*» che corrisponde a qualsiasi cosa."

"Ma cominciamo dall'inizio."

"Immagina di avere una classe Guerriero e un metodo che determina quale dei due guerrieri è più forte. Ecco come potrebbe apparire:"

Esempio 1
class WarriorManager
{
 public static boolean fight(Warrior w1, Warrior w2)
 {
  return w1.power > w2.power;
 }
}
Esempio di chiamata al metodo
MagicWarrior mag = new MagicWarrior();
ArcherWarrior archer = new ArcherWarrior();

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

"MagicWarrior e ArcherWarrior ereditano entrambi Warrior. ."

"È un po' semplicistico, ma per esempio andrà bene."

"OK."

"Ora supponi di aver deciso di adottare un metodo simile per la situazione in cui più guerrieri si sono uniti alla mischia."

Esempio 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 
 }
}
Esempio di chiamata al metodo
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!

"Ma qui incontri un errore di compilazione e sei perplesso su cosa potrebbe esserci di sbagliato."

"Il fatto è che MagicWarrior eredita Warrior e gli oggetti MagicWarrior possono essere passati al metodo fight(Warrior, Warrior)."

"Ma List<MagicWarior> non eredita List<Warrior> . Quindi, non puoi passarlo lì!"

"Cosa vuoi dire che non lo eredita?"

"Intendo questo: uno è un elenco e l'altro è un elenco, ma hanno parametri di tipo."

"Hai ragione. In qualche modo non l'ho notato subito. Quindi, c'è già una soluzione a questo problema?"

"Sì. Devi usare una struttura più complessa. Ecco come appare:"

Esempio 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 «? estende il Guerriero» significa «qualsiasi tipo che eredita il Guerriero»"

"In altre parole, ora puoi passare un List<MagicWarrior> e un List<ArcherWarrior> e tutto funzionerà."

"È così fantastico. Meno problemi del genere, meglio è."

"È così che mi sento anch'io."

"Ma cosa succede se non ho bisogno di qualcosa che erediti Warrior? Cosa succede se voglio essere in grado di passare qualsiasi List con qualsiasi parametro di tipo al metodo? È consentito?"

"Sì, ci sono due forme di notazione per farlo:"

List<? extends Object>
List<?>

"Entrambi significano la stessa cosa, quindi di solito viene usata la seconda versione."

"È tutto per oggi."

"Grazie, Ellie. Ho davvero imparato molto oggi."