CodeGym /Java Blog /Random-IT /Ampliamento e restringimento dei tipi di riferimento
John Squirrels
Livello 41
San Francisco

Ampliamento e restringimento dei tipi di riferimento

Pubblicato nel gruppo Random-IT
CIAO! In una lezione passata, abbiamo discusso del cast di tipi primitivi. Ricordiamo brevemente ciò che è stato discusso. Ampliamento e restringimento dei tipi di riferimento - 1Abbiamo immaginato i tipi primitivi (in questo caso, i tipi numerici) come pupazzi nidificanti di dimensioni variabili a seconda della quantità di memoria che occupano. Come ricorderete, mettere una bambola più piccola dentro una più grande è semplice sia nella vita reale che nella programmazione Java.

public class Main {
   public static void main(String[] args) {
       int bigNumber = 10000000;
       short smallNumber = (short) bigNumber;
       System.out.println(smallNumber);
   }
}
Questo è un esempio di conversione o ampliamento automatico . Succede da solo, quindi non è necessario scrivere codice aggiuntivo. Alla fine, non stiamo facendo nulla di insolito: stiamo solo mettendo una bambola più piccola in una bambola più grande. È un'altra questione se proviamo a fare il contrario e mettiamo una bambola russa più grande in una bambola più piccola. Non puoi farlo nella vita reale, ma nella programmazione puoi. Ma c'è una sfumatura. Se proviamo a inserire an intin una shortvariabile, le cose non vanno così bene per noi. Dopotutto, la shortvariabile contiene solo 16 bit di informazioni, ma an intoccupa 32 bit! Di conseguenza, il valore passato è distorto. Il compilatore ci darà un errore (" Amico, stai facendo qualcosa di sospetto!'). Ma se indichiamo esplicitamente il tipo in cui stiamo convertendo il nostro valore, andrà avanti ed eseguirà l'operazione.

public class Main {

   public static void main(String[] args) {

       int bigNumber = 10000000;

       bigNumber = (short) bigNumber;

       System.out.println(bigNumber);

   }

}
Questo è proprio quello che abbiamo fatto nell'esempio sopra. L'operazione è stata eseguita, ma poiché la shortvariabile può contenere solo 16 dei 32 byte, il valore finale è distorto e otteniamo il numero -27008 . Tale operazione è chiamata conversione esplicita o restringimento .

Esempi di ampliamento e restringimento dei tipi di riferimento

Parliamo ora degli stessi operatori applicati non ai tipi primitivi, ma agli oggetti e alle variabili di riferimento ! Come funziona in Java? In realtà è abbastanza semplice. Ci sono oggetti che non sono correlati. Sarebbe logico presumere che non possono essere convertiti l'uno nell'altro, né esplicitamente né automaticamente:

public class Cat {
}

public class Dog {
}

public class Main {

   public static void main(String[] args) {

       Cat cat = new Dog(); // Error!

   }

}
Qui, ovviamente, otteniamo un errore. Le classi Cate Dognon sono correlate tra loro e non abbiamo scritto un "convertitore" per passare dall'una all'altra. Ha senso che non possiamo farlo: il compilatore non ha idea di come convertire questi oggetti da un tipo all'altro. Se gli oggetti sono correlati, beh, questa è un'altra questione! Relativo come? Soprattutto per via ereditaria. Proviamo a usare l'ereditarietà per creare un piccolo sistema di classi. Avremo una classe comune per rappresentare gli animali:

public class Animal {

   public void introduce() {

       System.out.println("I'm Animal");
   }
}
Tutti sanno che gli animali possono essere addomesticati (animali domestici) o selvatici:

public class WildAnimal extends Animal {

   public void introduce() {

       System.out.println("I'm WildAnimal");
   }
}

public class Pet extends Animal {

   public void introduce() {

       System.out.println("I'm Pet");
   }
}
Ad esempio, prendi i cani: abbiamo cani domestici e coyote:

public class Dog extends Pet {

   public void introduce() {

       System.out.println("I'm Dog");
   }
}



public class Coyote extends WildAnimal {

   public void introduce() {

       System.out.println ("I'm Coyote");
   }
}
Abbiamo scelto appositamente le classi più basilari per renderle più facili da capire. Non abbiamo davvero bisogno di alcun campo e un metodo è sufficiente. Proviamo ad eseguire questo codice:

public class Main {

   public static void main(String[] args) {

       Animal animal = new Pet();
       animal.introduce();
   }
}
Cosa pensi verrà visualizzato sulla console? Verrà richiamato il introducemetodo della Petclasse o la classe? AnimalProva a giustificare la tua risposta prima di continuare a leggere. Ed ecco il risultato! I'm Pet Perché l'abbiamo preso? È tutto semplice. Abbiamo una variabile genitore e un oggetto discendente. Scrivendo,

Animal animal = new Pet();
abbiamo ampliato un Petriferimento e lo abbiamo assegnato a una Animalvariabile. Come per i tipi primitivi, i tipi di riferimento vengono automaticamente ampliati in Java. Non è necessario scrivere codice aggiuntivo per realizzarlo. Ora abbiamo un oggetto discendente assegnato a un riferimento genitore. Di conseguenza, vediamo che la chiamata al metodo viene effettuata sulla classe discendente. Se ancora non capisci completamente perché questo codice funziona, riscrivilo in un linguaggio semplice:

Animal animal = new DomesticatedAnimal();
Non ci sono problemi con questo, giusto? Immagina che questa sia la vita reale e che il riferimento sia semplicemente un'etichetta di carta con scritto "Animale". Se prendi quel pezzo di carta e lo attacchi al collare di qualsiasi animale domestico, tutto andrà bene. Dopotutto, ogni animale domestico è un animale! Il processo inverso - spostando l'albero ereditario ai discendenti - sta restringendo:

public class Main {

   public static void main(String[] args) {

       WildAnimal wildAnimal = new Coyote();

       Coyote coyote = (Coyote) wildAnimal;

       coyote.introduce();
   }
}
Come puoi vedere, qui indichiamo chiaramente la classe in cui vogliamo convertire il nostro oggetto. In precedenza avevamo una WildAnimalvariabile e ora abbiamo un Coyote, che è più in basso nell'albero di ereditarietà. Ha senso che senza un'indicazione esplicita il compilatore non permetta tale operazione, ma se indichiamo il tipo tra parentesi, allora tutto funziona. Ampliamento e restringimento delle tipologie di riferimento - 2Consideriamo un altro esempio più interessante:

public class Main {

   public static void main(String[] args) {

       Pet pet = new Animal(); // Error!
   }
}
Il compilatore genera un errore! Ma perché? Perché stai tentando di assegnare un oggetto genitore a un riferimento discendente. In altre parole, stai cercando di fare qualcosa del genere:

DomesticatedAnimal domesticatedAnimal = new Animal();
Bene, forse tutto funzionerà se specifichiamo esplicitamente il tipo in cui stiamo cercando di convertire? Ha funzionato con i numeri: proviamo! :)

public class Main {

   public static void main(String[] args) {

       Pet pet = (Pet) new Animal();
   }
}
Eccezione nel thread "main" java.lang.ClassCastException: Animal non può essere trasmesso a Pet Error! Il compilatore questa volta non ci ha urlato contro, ma alla fine abbiamo fatto un'eccezione. Conosciamo già il motivo: stiamo cercando di assegnare un oggetto genitore a un riferimento discendente. Ma perché esattamente non puoi farlo? Perché non tutti gli animali sono animali domestici. Hai creato un Animaloggetto e stai cercando di assegnarlo a una Petvariabile. Un coyote è anche un Animal, ma non è un Pet. In altre parole, quando scrivi

Pet pet = (Pet) new Animal();
new Animal()potrebbe rappresentare qualsiasi animale, non necessariamente un animale domestico! Naturalmente, la tua Pet petvariabile è adatta solo per memorizzare animali domestici (e i loro discendenti) e non qualsiasi tipo di animale. Ecco perché è stata creata un'eccezione Java speciale, ClassCastException, per i casi in cui si verifica un errore durante il casting delle classi. Rivediamolo di nuovo per rendere le cose più chiare. Un riferimento padre può puntare a istanze di una classe discendente:

public class Main {

   public static void main(String[] args) {

       Pet pet = new Pet();
       Animal animal = pet;

       Pet pet2 = (Pet) animal;
       pet2.introduce();
   }
}
Ad esempio, qui non abbiamo problemi. Abbiamo un Petoggetto referenziato da una Petvariabile. Più tardi, un Animalriferimento indicò lo stesso oggetto. Successivamente, convertiamo animalin un file Pet. A proposito, perché ha funzionato per noi? L'ultima volta abbiamo avuto un'eccezione! Perché questa volta il nostro oggetto originale è un Pet!

Pet pet = new Pet();
Ma nell'ultimo esempio, era un Animaloggetto:

Pet pet = (Pet) new Animal();
Non è possibile assegnare un oggetto antenato a una variabile discendente. Puoi fare il contrario.
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION