Lijevanje tipa objekta u Javi

1. Pregled

Sustav Java tipa sastoji se od dvije vrste tipova: primitiva i referenci.

U ovom smo članku pokrili primitivne pretvorbe i usredotočit ćemo se na ovdje emitirane reference kako bismo stekli dobro razumijevanje načina na koji Java obrađuje tipove.

2. Primitivno naspram reference

Iako primitivne pretvorbe i lijevanje referentnih varijabli mogu izgledati slično, oni su sasvim različiti koncepti.

U oba slučaja jednu vrstu pretvaramo u drugu. Ali, na pojednostavljeni način, primitivna varijabla sadrži svoju vrijednost, a pretvorba primitivne varijable znači nepovratne promjene u njezinoj vrijednosti:

dvostruki myDouble = 1,1; int myInt = (int) myDouble; assertNotEquals (myDouble, myInt);

Nakon pretvorbe u gornjem primjeru, myInt varijabla je 1, i ne možemo vratiti prethodnu vrijednost 1.1 iz toga.

Referentne varijable su različite; referentna varijabla odnosi se samo na objekt, ali ne sadrži sam objekt.

A lijevanje referentne varijable ne dodiruje objekt na koji se odnosi, već samo označava ovaj objekt na drugi način, proširujući ili sužavajući mogućnosti rada s njim. Upcasting sužava popis metoda i svojstava dostupnih ovom objektu, a downcasting ga može proširiti.

Referenca je poput daljinskog upravljača objekta. Daljinski upravljač ima više ili manje gumba ovisno o svojoj vrsti, a sam objekt pohranjen je u hrpu. Kada radimo lijevanje, mijenjamo vrstu daljinskog upravljača, ali ne mijenjamo sam objekt.

3. Ažuriranje

Lijevanje iz podrazreda u superrazred naziva se nadogradnjom. Tipično, nadogradnju implicitno izvodi kompajler.

Ažuriranje je usko povezano s nasljeđivanjem - još jednim temeljnim konceptom na Javi. Uobičajeno je koristiti referentne varijable za upućivanje na određeniji tip. I svaki put kad to učinimo, događa se implicitno nadogradnja.

Da demonstriramo nadogradnju, definirajmo Životinja razred:

javna klasa Životinja {javna praznina jesti () {// ...}}

Sada produžimo Životinja:

javna klasa Mačka produžuje Životinja {javna praznina jesti () {// ...} javna praznina mijau () {// ...}}

Sada možemo stvoriti objekt od Mačka razreda i dodijelite ga referentnoj varijabli tipa Mačka:

Mačka mačka = nova Mačka ();

A također ga možemo dodijeliti referentnoj varijabli tipa Životinja:

Životinjska životinja = mačka;

U gornjem zadatku odvija se implicitno nadogradnja. Mogli bismo to učiniti eksplicitno:

životinja = (Životinja) mačka;

Ali nema potrebe za eksplicitnim odbacivanjem nasljednog stabla. Prevoditelj to zna mačka je Životinja i ne prikazuje nikakve pogreške.

Imajte na umu da se ta referenca može odnositi na bilo koju podvrstu deklariranog tipa.

Koristeći ažuriranje, ograničili smo broj dostupnih metoda Mačka instance, ali nisu promijenili samu instancu. Sada ne možemo učiniti ništa što je specifično za Mačka - ne možemo se pozivati Mijau() na životinja varijabilna.

Iako Mačka objekt ostaje Mačka objekt, pozivanje Mijau() bi uzrokovao pogrešku kompajlera:

// animal.meow (); Metoda meow () nije definirana za tip Životinja

Zazivati Mijau() moramo se spustiti životinja, a to ćemo učiniti kasnije.

Ali sada ćemo opisati što nam daje nadogradnju. Zahvaljujući nadogradnji, možemo iskoristiti polimorfizam.

3.1. Polimorfizam

Definirajmo još jedan podrazred od Životinja, a Pas razred:

pas javne klase produžuje životinju {javna praznina jesti () {// ...}}

Sada možemo definirati feed () metoda koja tretira sve mačke i pse životinje:

javna klasa AnimalFeeder {javna void feed (Popis životinja) {animals.forEach (animal -> {animal.eat ();}); }}

Ne želimo AnimalFeeder brinuti se za koji životinja je na popisu - a Mačka ili a Pas. U feed () metoda svi su životinje.

Implicitno nadogradnja javlja se kada u objekt dodamo objekte određene vrste životinje popis:

Popis životinja = novi ArrayList (); animals.add (novi Cat ()); animals.add (novi Pas ()); novi AnimalFeeder (). feed (životinje);

Dodamo mačke i pse i oni su prepušteni Životinja tip implicitno. Svaki Mačka je Životinja i svaki Pas je Životinja. Polimorfni su.

Inače, svi su Java objekti polimorfni jer je svaki objekt Objekt barem. Možemo dodijeliti primjerak Životinja na referentnu varijablu od Objekt tipa i sastavljač se neće žaliti:

Objektni objekt = nova Animal ();

Zato svi Java objekti koje kreiramo već imaju Objekt određene metode, na primjer, toString ().

Česta je nadogradnja na sučelje.

Možemo stvarati Mew sučelje i napraviti Mačka provesti ga:

javno sučelje Mew {public void meow (); } javna klasa Mačka proširuje Animal implementira Mew {public void eat () {// ...} public void meow () {// ...}}

Sad bilo koji Mačka objekt se također može nadograditi na Mew:

Mew mew = nova Mačka ();

Mačka je Mew, ažuriranje je legalno i vrši se implicitno.

Tako, Mačka je Mew, Životinja, Objekt, i Mačka. Može se dodijeliti referentnim varijablama sve četiri vrste u našem primjeru.

3.2. Nadjačavanje

U gornjem primjeru, jesti() metoda je nadjačana. To znači da iako jesti() poziva se na varijablu Životinja tipa, posao se vrši metodama koje se prizivaju na stvarnim objektima - mačkama i psima:

javna void feed (Popis životinja) {animals.forEach (animal -> {animal.eat ();}); }

Ako svojim predavanjima dodamo neke zapisnike, vidjet ćemo to MačkaI PasMetode se nazivaju:

web - 2018-02-15 22: 48: 49,354 [glavna] INFO com.baeldung.casting.Cat - mačka jede mrežu - 2018-02-15 22: 48: 49,363 [glavna] INFO com.baeldung.casting.Dog - pas jede 

Da rezimiramo:

  • Referentna varijabla može se odnositi na objekt ako je objekt istog tipa kao varijabla ili ako je podtip
  • Ažuriranje se događa implicitno
  • Svi su Java objekti polimorfni i mogu se tretirati kao objekti nadtipa zbog nadogradnje

4. Downcasting

Što ako želimo koristiti varijablu type Životinja pozvati metodu dostupnu samo Mačka razred? Ovdje dolazi do obaranja. To je lijevanje iz superrazreda u podrazred.

Uzmimo primjer:

Životinjska životinja = nova Mačka ();

Mi to znamo životinja varijabla odnosi se na instancu Mačka. I mi se želimo zazivati MačkaS Mijau() metoda na životinja. Ali sastavljač se žali na to Mijau() metoda ne postoji za tip Životinja.

Zvati Mijau() trebali bismo se spustiti životinja do Mačka:

((Mačka) životinja) .meow ();

Unutarnje zagrade i vrsta koju sadrže ponekad se nazivaju i operatorom lijevanja. Imajte na umu da su za sastavljanje koda potrebne i vanjske zagrade.

Prepišimo prethodno AnimalFeeder primjer sa Mijau() metoda:

javna klasa AnimalFeeder {javna void feed (Popis životinja) {animals.forEach (animal -> {animal.eat (); if (animal instanceof Cat) {((Cat) animal) .meow ();}}); }}

Sada dobivamo pristup svim metodama dostupnim Mačka razred. Pogledajte zapisnik da biste se to uvjerili Mijau() zapravo se zove:

web - 2018-02-16 18: 13: 45,445 [glavna] INFO com.baeldung.casting.Cat - mačka jede mrežu - 2018-02-16 18: 13: 45,454 [glavna] INFO com.baeldung.casting.Cat - mijau web - 16.02.2018 18: 13: 45,455 [glavna] INFO com.baeldung.casting.Dog - pas jede

Imajte na umu da u gornjem primjeru pokušavamo spustiti samo one objekte koji su stvarno primjeri Mačka. Da bismo to učinili, koristimo operator instanceof.

4.1. instanceof Operater

Često koristimo instanceof operator prije spuštanja da provjeri pripada li objekt određenoj vrsti:

if (animal instanceof Cat) {((Cat) animal) .meow (); }

4.2. ClassCastException

Da nismo provjerili vrstu s instanceof operatora, sastavljač se ne bi žalio. Ali za vrijeme izvođenja postojala bi iznimka.

Da bismo to demonstrirali, uklonimo instanceof operator iz gornjeg koda:

javna praznina un CheckFeed (popis životinja) {životinje.zaEach (životinja -> {životinja.eat (); ((mačka) životinja) .meow ();}); }

Ovaj se kod kompajlira bez problema. Ali ako ga pokušamo pokrenuti, vidjet ćemo iznimku:

java.lang.ClassCastException: com.baeldung.casting.Dog ne može se baciti na com.baeldung.casting.Mačka

To znači da pokušavamo pretvoriti objekt koji je instanca Pas u a Mačka primjer.

ClassCastException 'uvijek se bacaju u vrijeme izvođenja ako se vrsta na koju smo oboreni ne podudara s vrstom stvarnog objekta.

Imajte na umu da ako pokušavamo spustiti na nepovezanu vrstu, kompajler to neće dopustiti:

Životinjska životinja; Niz s = (niz) životinja;

Kompilator kaže "Ne može se emitirati iz životinje u niz".

Da bi se kod kompajlirao, obje vrste trebale bi biti u istom stablu nasljeđivanja.

Sažmimo:

  • Downcasting je potreban da bi se dobio pristup članovima specifičnim za podrazred
  • Downcasting se vrši pomoću operatora cast
  • Da bismo sigurno spustili objekt, trebamo instanceof operater
  • Ako se stvarni objekt ne podudara s vrstom kojeg smo spustili, tada ClassCastException bit će bačen za vrijeme izvođenja

5. cast () Metoda

Postoji još jedan način za lijevanje predmeta pomoću metoda Razred:

javna praznina whenDowncastToCatWithCastMethod_thenMeowIsCalled () {Životinjska životinja = nova Mačka (); if (Cat.class.isInstance (životinja)) {Cat cat = Cat.class.cast (životinja); cat.meow (); }}

U gornjem primjeru, cast () i isInstance () koriste se metode umjesto cast i instanceof operatora odgovarajuće.

Uobičajeno je za upotrebu cast () i isInstance () metode s generičkim tipovima.

Stvarajmo AnimalFeederGeneric razred sa feed () metoda koja "hrani" samo jednu vrstu životinja - mačke ili pse, ovisno o vrijednosti parametra tipa:

javna klasa AnimalFeederGeneric {tip privatne klase; javni AnimalFeederGeneric (tip klase) {this.type = type; } javni popis feeda (popis životinja) {popis popisa = novi ArrayList (); animals.forEach (animal -> {if (type.isInstance (animal)) {T objAsType = type.cast (animal); list.add (objAsType);}}); popis za povratak; }}

The feed () metoda provjerava svaku životinju i vraća samo one koji su primjeri T.

Imajte na umu da Razred instancu također treba proslijediti generičkoj klasi jer je ne možemo dobiti iz parametra type T. U našem primjeru prosljeđujemo ga u konstruktoru.

Idemo napraviti T jednak Mačka i pobrinite se da metoda vraća samo mačke:

@Test public void whenParameterCat_thenOnlyCatsFed () {Popis životinja = novi ArrayList (); animals.add (novi Cat ()); animals.add (novi Pas ()); AnimalFeederGeneric catFeeder = novi AnimalFeederGeneric (Cat.class); Popis fedAnimals = catFeeder.feed (životinje); assertTrue (fedAnimals.size () == 1); assertTrue (fedAnimals.get (0) instanca Mačke); }

6. Zaključak

U ovom temeljnom vodiču istražili smo što se nadograđuje, spušta, kako ih koristiti i kako vam ovi koncepti mogu pomoći da iskoristite polimorfizam.

Kao i uvijek, kôd za ovaj članak dostupan je na GitHubu.