1. Ma non è tutto.

Supponiamo che la Cowclasse abbia un printAll()metodo che chiama altri due metodi. Quindi il codice funzionerà in questo modo:

Codice Descrizione
class Cow
{
   public void printAll()
   {
      printColor();
      printName();
   }

   public void printColor ()
   {
      System.out.println("I'm a white whale");
   }

   public void printName()
   {
      System.out.println("I'm a cow");
   }
}

class Whale extends Cow
{
   public void printName()
   {
      System.out.println("I'm a whale");
   }
}
public static void main(String[] args)
{
   Whale whale = new Whale ();
   whale.printAll();
}
L'output dello schermo sarà:
I'm a white whale
I'm a whale

Si noti che quando il printAll()metodo nella Cowclasse viene chiamato su un Whaleoggetto, viene utilizzato il printNamemetodo dellaWhale classe, non quello nel Cowmetodo.

La cosa principale non è la classe in cui è scritto il metodo, ma piuttosto il tipo (classe) dell'oggetto su cui viene chiamato il metodo.

Solo i metodi non statici possono essere ereditati e sovrascritti. I metodi statici non vengono ereditati e pertanto non possono essere sovrascritti.

Ecco come Whaleappare la classe dopo aver applicato l'ereditarietà e l'override del metodo:

class Whale
{
   public void printAll()
   {
      printColor();
      printName();
   }

   public void printColor()
   {
      System.out.println("I'm a white whale");
   }

   public void printName()
   {
      System.out.println("I'm a whale");
   }
}
Ecco come Whaleappare la classe dopo aver applicato l'ereditarietà e l'override del metodo: Non conosciamo alcun vecchio printNamemetodo.

2. Tipografia

C'è un punto ancora più interessante qui. Poiché una classe eredita tutti i metodi e i dati della sua classe genitore, un riferimento a un oggetto della classe figlia può essere memorizzato in (assegnato a) variabili il cui tipo è lo stesso della classe genitore (e del genitore del genitore, ecc. — fino in Objectclasse). Esempio:

Codice Descrizione
public static void main(String[] args)
{
   Whale whale = new Whale();
   whale.printColor();
}
L'output dello schermo sarà:
I'm a white whale
public static void main(String[] args)
{
   Cow cow = new Whale();
   cow.printColor();
}
L'output dello schermo sarà:
I'm a white whale
public static void main(String[] args)
{
   Object o = new Whale();
   System.out.println(o.toString());
}
L'output dello schermo sarà:
Whale@da435a.

Il toString()metodo è ereditato dalla Objectclasse

Questa è una proprietà molto preziosa: poco dopo capirai come usarla nella pratica.


3. Chiamare un metodo su un oggetto

Quando un metodo viene chiamato su una variabile, il metodo viene effettivamente chiamato su un oggetto. Questo meccanismo è chiamato invio di metodi dinamici.

Ecco come appare:

Codice Descrizione
public static void main(String[] args)
{
   Whale whale = new Whale();
   whale.printName();
}
L'output dello schermo sarà:
I'm a whale
public static void main(String[] args)
{
   Cow cow = new Whale();
   cow.printName();
}
L'output dello schermo sarà:
I'm a whale

Si noti che l'implementazione specifica del printName()metodo che viene chiamato — quello in Cowo quello nella Whaleclasse — non è determinata dal tipo della variabile, ma dal tipo dell'oggetto a cui fa riferimento la variabile.

La Cowvariabile memorizza un riferimento a un Whaleoggetto e il printName()metodo definito nella Whaleclasse è ciò che viene chiamato.

Questo non è molto ovvio. Ricorda la regola principale:

L'insieme di metodi disponibili per essere chiamati su una variabile è determinato dal tipo della variabile. E l'implementazione specifica del metodo che viene chiamata è determinata dal tipo/classe dell'oggetto a cui fa riferimento la variabile.

Lo incontrerai sempre, quindi prima lo ricordi, meglio è.