JavaRush /Java Blog /Random-IT /Espansione e contrazione dei tipi di riferimento

Espansione e contrazione dei tipi di riferimento

Pubblicato nel gruppo Random-IT
Ciao! In una delle lezioni precedenti abbiamo discusso della fusione dei tipi primitivi. Ricordiamo brevemente di cosa stavamo parlando. Espansione e contrazione delle tipologie di riferimento - 1Abbiamo rappresentato i tipi primitivi (in questo caso numerici) come bambole nidificanti in base alla quantità di memoria che occupano. Come ricordi, posizionare una bambola da nidificazione più piccola in una più grande sarà semplice sia nella vita reale che nella programmazione Java.
public class Main {
   public static void main(String[] args) {
        short smallNumber = 100;
        int bigNumber =  smallNumber;
        System.out.println(bigNumber);
   }
}
Questo è un esempio di conversione automatica, o estensione . Succede da solo, quindi non è necessario scrivere codice aggiuntivo. Alla fine non facciamo nulla di insolito: inseriamo semplicemente una bambola da nidificazione più piccola in una bambola da nidificazione più grande. Un'altra questione è se proviamo a fare il contrario e mettiamo una matrioska grande in una più piccola. Questo non si può fare nella vita, ma nella programmazione si può fare. Ma c'è un avvertimento. Se proviamo a inserire un valore intin una variabile short, non funzionerà così facilmente. Dopotutto, in una variabile possono essere contenuti solo 16 bit di informazione short, ma il valore intoccupa 32 bit! Di conseguenza, il valore trasmesso sarà distorto. Il compilatore ci darà un errore (“ amico, stai facendo qualcosa di sospetto! ”), ma se specifichiamo esplicitamente a quale tipo stiamo trasmettendo il nostro valore, eseguirà comunque tale operazione.
public class Main {

   public static void main(String[] args) {

       int bigNumber = 10000000;

       bigNumber = (short) bigNumber;

       System.out.println(bigNumber);

   }

}
Nell'esempio sopra, abbiamo fatto proprio questo. L'operazione è stata completata, ma poiché shortsolo 16 dei 32 bit rientrano nella variabile, il valore finale è stato distorto e di conseguenza abbiamo ricevuto il numero -27008 . Questa operazione è chiamata conversione esplicita o restringimento .

Esempi di estensione e contrazione di tipi di riferimento

Ora parleremo delle stesse operazioni, ma applicabili non ai tipi primitivi, ma agli oggetti e alle variabili di riferimento ! Come funziona in Java? Abbastanza semplice in realtà. Ci sono oggetti che non sono correlati tra loro. Sarebbe logico supporre che non possano 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, otterremo un errore. Le classi non Catsono Dogcorrelate tra loro e non abbiamo scritto un "convertitore" dall'una all'altra. È logico che non saremo in grado di farlo: il compilatore non ha idea di come convertire questi oggetti tra loro. Un’altra cosa è se gli oggetti sono collegati tra loro! Come? Prima di tutto, usando l'ereditarietà. Proviamo a creare un sistema di piccole classi con ereditarietà. Avremo una classe generale che rappresenta gli animali:
public class Animal {

   public void introduce() {

       System.out.println("i'm Animal");
   }
}
Gli animali, come sai, sono domestici e 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, prendiamo i cani: un cane domestico e un 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");
   }
}
Le nostre classi sono volutamente le più primitive per renderle più facili da percepire. Non abbiamo davvero bisogno di campi qui e un metodo è sufficiente. Proviamo a eseguire il seguente codice:
public class Main {

   public static void main(String[] args) {

       Animal animal = new Pet();
       animal.introduce();
   }
}
Cosa pensi che verrà visualizzato sulla console? introduceLa classe Peto il metodo della classe funzioneranno Animal? Prova a motivare la tua risposta prima di continuare a leggere. E questo è il risultato! sono Pet Perché la risposta è venuta in questo modo? È semplice. Abbiamo una variabile genitore e un oggetto figlio. Scrivendo:
Animal animal = new Pet();
Abbiamo esteso un tipo di riferimentoPet e memorizzato il suo oggetto in una variabile Animal. Come con i tipi primitivi, l'estensione dei tipi di riferimento in Java viene eseguita automaticamente. Non è necessario scrivere codice aggiuntivo per questo. Ora abbiamo un oggetto figlio collegato al riferimento genitore e di conseguenza vediamo che il metodo viene chiamato sulla classe figlia. Se ancora non capisci appieno perché questo codice funziona, riscrivilo in un linguaggio semplice:
Животное животное = new ДомашнееЖивотное();
Non c'è problema con questo, giusto? Immagina che questa sia la vita reale e che il collegamento in questo caso sia una semplice etichetta di carta con la scritta "Animale". Se prendi un pezzo di carta del genere e lo attacchi al collare di qualsiasi animale domestico, andrà tutto bene. Qualsiasi animale domestico è pur sempre un animale! Il processo inverso, ovvero lo spostamento lungo l’albero ereditario verso gli eredi, è un restringimento:
public class Main {

   public static void main(String[] args) {

       WildAnimal wildAnimal = new Coyote();

       Coyote coyote = (Coyote) wildAnimal;

       coyote.introduce();
   }
}
Come puoi vedere, qui indichiamo esplicitamente a quale classe vogliamo trasmettere il nostro oggetto. In precedenza avevamo una variabile WildAnimal, e ora Coyote, che scende lungo l'albero dell'ereditarietà. È logico che il compilatore non salti un'operazione del genere senza un'indicazione esplicita, ma se specifichi il tipo tra parentesi, tutto funzionerà. Espansione e contrazione delle tipologie di riferimento - 2 Facciamo un altro esempio, più interessante:
public class Main {

   public static void main(String[] args) {

       Pet pet = new Animal();//error!
   }
}
Il compilatore genera un errore! Qual è il motivo? Il fatto è che stai cercando di assegnare un oggetto genitore a una variabile figlio. In altre parole, vuoi fare questo:
ДомашнееЖивотное домашнееЖивотное = new Животное();
Ma forse se indichiamo esplicitamente il tipo a cui stiamo cercando di eseguire il cast, riusciremo? I numeri sembrano funzionare, proviamolo! :)
public class Main {

   public static void main(String[] args) {

       Pet pet = (Pet) new Animal();
   }
}
Eccezione nel thread "main" java.lang.ClassCastException: l'animale non può essere lanciato su Pet Error! Questa volta il compilatore non si è lamentato, ma di conseguenza abbiamo ricevuto un'eccezione. Conosciamo già il motivo: stiamo cercando di assegnare un oggetto genitore a una variabile figlio. Perché, in effetti, ciò non può essere fatto? Perché non tutti gli animali sono animali domestici. Hai creato un oggetto Animale stai tentando di assegnarlo a una variabile Pet. Ma, per esempio, un coyote è anche Animal, ma non è Pet, un animale domestico. In altre parole, quando scrivi:
Pet pet = (Pet) new Animal();
new Animal()Qualsiasi animale può essere lì e non deve essere necessariamente domestico! Naturalmente, la tua variabile Pet petè adatta solo per conservare gli animali domestici (e i loro discendenti) e non per tutti. Pertanto, per questi casi, in Java è stata creata un'eccezione speciale: ClassCastExceptionun errore durante il casting delle classi. Ripetiamolo per renderlo più chiaro. Una variabile genitore (riferimento) può puntare a un oggetto 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, non avremo problemi qui. Abbiamo un oggetto Peta cui punta un collegamento Pet. Poi un nuovo collegamento cominciò a puntare allo stesso oggetto Animal. Dopodiché eseguiamo la conversione animalin Pet. A proposito, perché l'abbiamo fatto? L'ultima volta abbiamo avuto un'eccezione! Perché questa volta il nostro oggetto originale è Pet pet!
Pet pet =  new Pet();
E nell'esempio precedente era un oggetto Animal:
Pet pet = (Pet) new Animal();
A una variabile discendente non può essere assegnato un oggetto antenato. Al contrario, puoi farlo.
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION