1. Tipografia
Le variabili che memorizzano tipi di riferimento (classi) possono anche essere convertite in tipi diversi. Ma questo funziona solo all'interno di una singola gerarchia di tipo. Diamo un'occhiata a un semplice esempio. Supponiamo di avere la seguente gerarchia di classi, in cui le classi sottostanti ereditano le classi sopra.
Anche il typecasting dei tipi di riferimento e di quelli primitivi è classificato come ampliamento e restringimento.
Vediamo che la classe Cat eredita la classe Pet e la classe Pet, a sua volta, eredita la classe Animal.
Se scriviamo codice come questo:
Animal kitten = new Cat();
Questa è una conversione di tipo allargamento . È anche chiamato cast implicito. Abbiamo ampliato il riferimento cat in modo che ora si riferisca a un oggetto Cat . Con una conversione di tipo come questa, non saremo in grado di utilizzare il riferimento gattino per chiamare metodi presenti nella classe Cat ma assenti nella classe Animal .
Una conversione di restringimento (o cast esplicito) avviene nella direzione opposta:
Cat cat = (Cat) kitten;
Abbiamo indicato esplicitamente che vogliamo eseguire il cast del riferimento memorizzato nella variabile gattino (il cui tipo è Animal ) al tipo Cat .
2. Verifica del tipo di un oggetto
Ma devi stare molto attento qui. Se lo fai:
Animal beast = new Cat();
Wolf grayWolf = (Wolf) beast;
Il compilatore consentirà questo codice, ma ci sarà un errore durante l'esecuzione del programma! La JVM genererà un'eccezione:
Exception in thread "main" java.lang.ClassCastException: Cat cannot be cast to a Wolf
I riferimenti a un oggetto Cat possono essere memorizzati solo in variabili il cui tipo è un antenato della classe Cat: Pet, Animal o Object.
Perché?
Il punto rilevante qui è che un riferimento a un oggetto viene utilizzato per fare riferimento ai metodi e alle variabili di quell'oggetto . E non ci saranno problemi se usiamo una variabile Animal per memorizzare un riferimento a un oggetto Cat: il tipo Cat ha sempre variabili e metodi del tipo Animal — li ha ereditati!
Ma se la JVM ci permettesse di memorizzare un riferimento a un oggetto Cat in una variabile Wolf, allora potremmo avere una situazione in cui potremmo provare a usare la variabile grayWolf per chiamare un metodo che non esiste nell'oggetto Cat memorizzato in quella variabile . Ecco perché questa disposizione non è consentita.
Java ha un operatore speciale instanceof
che ti consente di verificare se un oggetto è di un certo tipo e quindi può essere memorizzato in una variabile di un certo tipo. Sembra abbastanza semplice:
variable instanceof Type
Esempio:
Animal beast = new Cat();
if (beast instanceof Wolf)
{
Wolf grayWolf = (Wolf) beast;
}
Questo codice non causerà errori, nemmeno in fase di esecuzione.
Ecco alcuni altri esempi che illustrano la situazione:
Conversione del tipo di ampliamento | Descrizione |
---|---|
|
Questa è una classica conversione di ampliamento: non è richiesto alcun operatore di conversione del tipo. Ora solo i metodi definiti nella Sulla |
Conversione del tipo di restringimento | |
|
Conversione di restringimento classica: è necessario aggiungere un controllo del tipo e un operatore di cast. La Cow cow variabile memorizza un riferimento a un Whale oggetto. Verifichiamo che questo sia il caso e quindi eseguiamo una conversione di tipo (restringente). O come viene anche chiamato:
un cast tipo
.
|
|
È possibile restringere un tipo di riferimento senza controllare il tipo dell'oggetto. Se la cow variabile fa riferimento a un oggetto che non è a Whale , InvalidClassCastException verrà generato un oggetto. |
3. Chiamare il metodo originale: la super
parola chiave
Quando sovrascriviamo il metodo di una classe genitore, a volte invece di sostituirlo con il nostro, vogliamo solo integrarlo leggermente.
Sarebbe bello se potessimo inserire il metodo della classe genitore nel nostro metodo e quindi eseguire parte del nostro codice. O forse prima esegui il nostro codice e poi chiama il metodo della classe genitore.
E Java ci consente proprio questo. Per chiamare un metodo della classe genitore, procedere come segue:
super.method(arguments);
Esempi:
class PeaceTime
{
public double getPi()
{
return 3.14;
}
}
class WarTime extends PeaceTime
{
public double getPi()
{
return super.getPi()*2; // 3.14*2
}
}
In tempo di guerra, il valore di Pi
può essere maggiore di 6! Certo, stiamo scherzando, ma questo esempio dimostra come tutto questo può funzionare.
Ecco un altro paio di esempi per chiarire un po' le cose:
Codice | Descrizione |
---|---|
|
Cow e Whale classi |
|
L'output dello schermo sarà:
|
Questa è roba difficile. Onestamente, è una delle cose più difficili in OOP . Detto questo, devi conoscerlo e capirlo.
GO TO FULL VERSION