CodeGym /Java Blog /Willekeurig /Verbreding en versmalling van referentietypes
John Squirrels
Niveau 41
San Francisco

Verbreding en versmalling van referentietypes

Gepubliceerd in de groep Willekeurig
Hoi! In een eerdere les hebben we gesproken over het casten van primitieve typen. Laten we in het kort terughalen wat er is besproken. Verbreding en versmalling van referentietypes - 1We stelden ons primitieve typen (in dit geval numerieke typen) voor als nestpoppen die in grootte variëren afhankelijk van de hoeveelheid geheugen die ze innemen. Zoals je je zult herinneren, is het plaatsen van een kleinere pop in een grotere eenvoudig, zowel in het echte leven als in Java-programmering.

public class Main {
   public static void main(String[] args) {
       int bigNumber = 10000000;
       short smallNumber = (short) bigNumber;
       System.out.println(smallNumber);
   }
}
Dit is een voorbeeld van automatische conversie of verbreding . Het gebeurt vanzelf, dus u hoeft geen extra code te schrijven. Uiteindelijk doen we niets ongewoons: we stoppen gewoon een kleinere pop in een grotere pop. Het is een andere zaak als we het tegenovergestelde proberen te doen en een grotere Russische pop in een kleinere pop stoppen. Dat kun je in het echte leven niet doen, maar bij het programmeren wel. Maar er is één nuance. Als we proberen een intin een shortvariabele te stoppen, gaat het niet zo soepel voor ons. De variabele bevat immers shortmaar 16 bits informatie, maar an intneemt 32 bits in beslag! Als gevolg hiervan wordt de doorgegeven waarde vervormd. De compiler geeft ons een foutmelding (' Kerel, je doet iets verdachts!'). Maar als we expliciet aangeven naar welk type we onze waarde converteren, zal het doorgaan en de bewerking uitvoeren.

public class Main {

   public static void main(String[] args) {

       int bigNumber = 10000000;

       bigNumber = (short) bigNumber;

       System.out.println(bigNumber);

   }

}
Dat is precies wat we deden in het bovenstaande voorbeeld. De bewerking is uitgevoerd, maar omdat de shortvariabele slechts 16 van de 32 bytes kan bevatten, is de uiteindelijke waarde vervormd en krijgen we het getal -27008 . Een dergelijke bewerking wordt een expliciete conversie of vernauwing genoemd .

Voorbeelden van verbreding en versmalling van referentietypes

Laten we het nu hebben over dezelfde operatoren die niet worden toegepast op primitieve typen, maar op objecten en referentievariabelen ! Hoe werkt dit op Java? Het is eigenlijk heel simpel. Er zijn objecten die niets met elkaar te maken hebben. Het zou logisch zijn om aan te nemen dat ze niet naar elkaar kunnen worden omgezet, noch expliciet, noch automatisch:

public class Cat {
}

public class Dog {
}

public class Main {

   public static void main(String[] args) {

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

   }

}
Hier krijgen we natuurlijk een foutmelding. De klassen Caten Dogzijn niet aan elkaar gerelateerd en we hebben geen 'converter' geschreven om van de ene naar de andere over te gaan. Het is logisch dat we dit niet kunnen doen: de compiler heeft geen idee hoe hij deze objecten van het ene type naar het andere moet converteren. Als de objecten gerelateerd zijn, nou, dat is een andere zaak! Hoe gerelateerd? Vooral door erfenis. Laten we proberen overerving te gebruiken om een ​​klein systeem van klassen te maken. We hebben een gemeenschappelijke klasse om dieren te vertegenwoordigen:

public class Animal {

   public void introduce() {

       System.out.println("I'm Animal");
   }
}
Iedereen weet dat dieren gedomesticeerd (huisdieren) of wild kunnen zijn:

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");
   }
}
Neem bijvoorbeeld hoektanden - we hebben huishonden en coyotes:

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");
   }
}
We hebben specifiek de meest elementaire klassen gekozen om ze gemakkelijker te begrijpen te maken. We hebben niet echt velden nodig, en één methode is voldoende. Laten we proberen deze code uit te voeren:

public class Main {

   public static void main(String[] args) {

       Animal animal = new Pet();
       animal.introduce();
   }
}
Wat denk je dat er op de console wordt weergegeven? Wordt de introducemethode van de Petklasse of de Animalklasse aangeroepen? Probeer je antwoord te onderbouwen voordat je verder leest. En hier is het resultaat! I'm Pet Waarom hebben we dat gekregen? Het is allemaal eenvoudig. We hebben een parent-variabele en een descendant-object. Door te schrijven,

Animal animal = new Pet();
we hebben een Petreferentie verbreed en toegewezen aan een Animalvariabele. Net als bij primitieve typen, worden referentietypen automatisch verbreed in Java. U hoeft geen aanvullende code te schrijven om dit mogelijk te maken. Nu hebben we een afstammeling object toegewezen aan een bovenliggende referentie. Als gevolg hiervan zien we dat de methodeaanroep wordt gedaan op de afstammelingklasse. Als u nog steeds niet helemaal begrijpt waarom deze code werkt, herschrijf deze dan in gewone taal:

Animal animal = new DomesticatedAnimal();
Hier is toch geen probleem mee? Stel je voor dat dit het echte leven is, en de referentie is gewoon een papieren label met 'Animal' erop geschreven. Als je dat stuk papier pakt en het aan de halsband van een huisdier bevestigt, zal alles kloppen. Elk huisdier is tenslotte een dier! Het omgekeerde proces - naar beneden gaan in de erfenisboom naar afstammelingen - wordt smaller:

public class Main {

   public static void main(String[] args) {

       WildAnimal wildAnimal = new Coyote();

       Coyote coyote = (Coyote) wildAnimal;

       coyote.introduce();
   }
}
Zoals je kunt zien, geven we hier duidelijk de klasse aan waarnaar we ons object willen converteren. We hadden eerder een WildAnimalvariabele, en nu hebben we een Coyote, die lager in de overervingsboom staat. Het is logisch dat zonder een expliciete indicatie de compiler een dergelijke bewerking niet toestaat, maar als we het type tussen haakjes aangeven, dan werkt alles. Verbreding en versmalling van referentietypes - 2Overweeg nog een interessanter voorbeeld:

public class Main {

   public static void main(String[] args) {

       Pet pet = new Animal(); // Error!
   }
}
De compiler genereert een fout! Maar waarom? Omdat u probeert een bovenliggend object toe te wijzen aan een afstammelingreferentie. Met andere woorden, je probeert zoiets als dit te doen:

DomesticatedAnimal domesticatedAnimal = new Animal();
Misschien werkt alles als we expliciet specificeren naar welk type we proberen te converteren? Dat werkte met cijfers — Laten we het eens proberen! :)

public class Main {

   public static void main(String[] args) {

       Pet pet = (Pet) new Animal();
   }
}
Uitzondering in thread "main" java.lang.ClassCastException: Animal kan niet naar Pet worden gecast Fout! De compiler schreeuwde deze keer niet tegen ons, maar we eindigden met een uitzondering. We weten de reden al: we proberen een bovenliggend object toe te wijzen aan een afstammelingreferentie. Maar waarom zou je dat precies niet kunnen? Omdat niet alle dieren gedomesticeerde dieren zijn. U hebt een Animalobject gemaakt en probeert dit toe te wijzen aan een Petvariabele. Een coyote is ook een Animal, maar het is geen Pet. Met andere woorden, als je schrijft

Pet pet = (Pet) new Animal();
new Animal()kan elk dier vertegenwoordigen, niet noodzakelijkerwijs een huisdier! Uiteraard is uw Pet petvariabele alleen geschikt voor het opslaan van huisdieren (en hun nakomelingen) en niet voor elk type dier. Daarom is er een speciale Java-uitzondering, ClassCastException, gemaakt voor gevallen waarin er een fout optreedt tijdens het casten van klassen. Laten we het nog eens bekijken om de zaken duidelijker te maken. Een parent-referentie kan verwijzen naar instanties van een afstammende klasse:

public class Main {

   public static void main(String[] args) {

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

       Pet pet2 = (Pet) animal;
       pet2.introduce();
   }
}
Hier hebben we bijvoorbeeld geen problemen. We hebben een Petobject waarnaar wordt verwezen door een Petvariabele. Later wees een Animalverwijzing naar hetzelfde object. Daarna converteren we animalnaar een Pet. Trouwens, waarom werkte dat voor ons? Vorige keer kregen we een uitzondering! Omdat dit keer ons oorspronkelijke object een Pet!

Pet pet = new Pet();
Maar in het laatste voorbeeld was het een Animalobject:

Pet pet = (Pet) new Animal();
U kunt een voorouderobject niet toewijzen aan een afstammelingvariabele. U kunt het tegenovergestelde doen.
Opmerkingen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION