Objektype Casting i Java

1. Oversigt

Java-typesystemet består af to slags typer: primitiver og referencer.

Vi dækkede primitive konverteringer i denne artikel, og vi vil fokusere på referencer, der castes her, for at få en god forståelse af, hvordan Java håndterer typer.

2. Primitiv vs. Reference

Selvom primitive konverteringer og referencevariabel casting kan se ens ud, er de helt forskellige koncepter.

I begge tilfælde “omdanner” vi den ene type til den anden. Men på en forenklet måde indeholder en primitiv variabel dens værdi, og konvertering af en primitiv variabel betyder irreversible ændringer i dens værdi:

dobbelt myDouble = 1.1; int myInt = (int) myDouble; assertNotEquals (myDouble, myInt);

Efter konverteringen i ovenstående eksempel, minInt variabel er 1, og vi kan ikke gendanne den tidligere værdi 1.1 fra det.

Referencevariabler er forskellige; referencevariablen refererer kun til et objekt, men indeholder ikke selve objektet.

At caste en referencevariabel berører ikke objektet, det refererer til, men mærker kun dette objekt på en anden måde, hvilket udvider eller indsnævrer mulighederne for at arbejde med det. Upcasting indsnævrer listen over metoder og egenskaber, der er tilgængelige for dette objekt, og downcasting kan udvide det.

En reference er som en fjernbetjening til et objekt. Fjernbetjeningen har flere eller færre knapper afhængigt af dens type, og selve objektet er gemt i en bunke. Når vi støber, ændrer vi typen af ​​fjernbetjening, men ændrer ikke selve objektet.

3. Upcasting

Casting fra en underklasse til en superklasse kaldes upcasting. Typisk udføres upcasting implicit af compileren.

Upcasting er tæt knyttet til arv - et andet kernekoncept i Java. Det er almindeligt at bruge referencevariabler til at henvise til en mere specifik type. Og hver gang vi gør dette, finder implicit upcasting sted.

Lad os definere en for at demonstrere upcasting Dyr klasse:

offentlig klasse Animal {public void eat () {// ...}}

Lad os nu udvide Dyr:

offentlig klasse Kat udvider Animal {public void eat () {// ...} public void meow () {// ...}}

Nu kan vi skabe et objekt af Kat klasse og tildele den til referencevariablen af ​​typen Kat:

Katkat = ny kat ();

Og vi kan også tildele det til referencevariablen af ​​typen Dyr:

Dyredyr = kat;

I ovenstående opgave finder implicit upcasting sted. Vi kunne gøre det eksplicit:

dyr = (dyr) kat;

Men der er ikke behov for eksplicit opkastning af arvetræet. Kompilatoren ved det kat er en Dyr og viser ingen fejl.

Bemærk, at referencen kan henvise til enhver undertype af den deklarerede type.

Ved hjælp af upcasting har vi begrænset antallet af tilgængelige metoder til Kat eksempel, men har ikke ændret selve forekomsten. Nu kan vi ikke gøre noget, der er specifikt for Kat - vi kan ikke påberåbe os mjau () på den dyr variabel.

Selvom Kat genstand forbliver Kat objekt, der ringer mjau () ville forårsage kompilatorfejl:

// animal.meow (); Metoden meow () er udefineret for typen Dyr

At påberåbe sig mjau () vi er nødt til at nedskyde dyr, og vi gør det senere.

Men nu beskriver vi, hvad der giver os upcasting. Takket være upcasting kan vi drage fordel af polymorfisme.

3.1. Polymorfisme

Lad os definere en anden underklasse af Dyr, a Hund klasse:

offentlig klasse Hund udvider Animal {public void eat () {// ...}}

Nu kan vi definere foder() metode, der behandler alle katte og hunde som dyr:

public class AnimalFeeder {public void feed (List animals) {animals.forEach (animal -> {animal.eat ();}); }}

Vi vil ikke AnimalFeeder at bekymre sig om hvilken dyr er på listen - a Kat eller a Hund. I foder() metode, de er alle dyr.

Implicit upcasting opstår, når vi tilføjer objekter af en bestemt type til dyr liste:

Liste dyr = ny ArrayList (); animals.add (ny kat ()); animals.add (ny hund ()); nyt AnimalFeeder (). foder (dyr);

Vi tilføjer katte og hunde, og de er upcast til Dyr skriv implicit. Hver Kat er en Dyr og hver Hund er en Dyr. De er polymorfe.

Forresten er alle Java-objekter polymorfe, fordi hvert objekt er et Objekt i det mindste. Vi kan tildele en instans af Dyr til referencevariablen på Objekt type, og kompilatoren klager ikke:

Objektobjekt = nyt dyr ();

Derfor har alle Java-objekter, vi opretter, allerede Objekt specifikke metoder, for eksempel toString ().

Opcasting til en grænseflade er også almindelig.

Vi kan skabe Mew interface og mærke Kat implementer det:

offentlig grænseflade Mew {public void meow (); } offentlig klasse Kat udvider dyreudstyr Mew {public void eat () {// ...} public void meow () {// ...}}

Nu nogen Kat objekt kan også kastes op til Mew:

Mew mew = ny kat ();

Kat er en Mew, upcasting er lovligt og udføres implicit.

Dermed, Kat er en Mew, Dyr, Objektog Kat. Det kan tildeles referencevariabler af alle fire typer i vores eksempel.

3.2. Tilsidesættelse

I eksemplet ovenfor er spise() metoden er tilsidesat. Dette betyder, at selvom spise() kaldes på variablen af Dyr type, arbejdet udføres ved hjælp af metoder påkaldt rigtige genstande - katte og hunde:

offentligt ugyldigt feed (liste dyr) {animals.forEach (animal -> {animal.eat ();}); }

Hvis vi føjer noget logning til vores klasser, ser vi det Kat'S og HundMetoder kaldes:

web - 2018-02-15 22: 48: 49,354 [main] INFO com.baeldung.casting.Cat - cat is eating web - 2018-02-15 22: 48: 49,363 [main] INFO com.baeldung.casting.Dog - hunden spiser 

At opsummere:

  • En referencevariabel kan henvise til et objekt, hvis objektet er af samme type som en variabel, eller hvis det er en undertype
  • Upcasting sker implicit
  • Alle Java-objekter er polymorfe og kan behandles som objekter af supertype på grund af opkastning

4. Nedkastning

Hvad hvis vi vil bruge variablen af ​​typen Dyr at påberåbe sig en metode, der kun er tilgængelig for Kat klasse? Her kommer downcasting. Det er castingen fra en superklasse til en underklasse.

Lad os tage et eksempel:

Dyredyr = ny kat ();

Vi ved det dyr variabel henviser til forekomsten af Kat. Og vi vil påberåbe os Kat'S mjau () metode til dyr. Men compileren klager over det mjau () metoden findes ikke for typen Dyr.

At ringe mjau () vi skal nedskyde dyr til Kat:

((Kat) dyr) .mia ();

De indre parenteser og typen, de indeholder, kaldes undertiden cast-operatøren. Bemærk, at eksterne parenteser også er nødvendige for at kompilere koden.

Lad os omskrive det forrige AnimalFeeder eksempel med mjau () metode:

public class AnimalFeeder {public void feed (List animals) {animals.forEach (animal -> {animal.eat (); if (animal instanceof Cat) {((Cat) animal) .meow ();}}); }}

Nu får vi adgang til alle tilgængelige metoder til Kat klasse. Se på logfilen for at sikre dig, at mjau () kaldes faktisk:

web - 2018-02-16 18: 13: 45,445 [main] INFO com.baeldung.casting.Cat - cat is eating web - 2018-02-16 18: 13: 45,454 [main] INFO com.baeldung.casting.Cat - meow web - 2018-02-16 18: 13: 45,455 [main] INFO com.baeldung.casting.Dog - hund spiser

Bemærk, at vi i ovenstående eksempel kun forsøger at nedsky de objekter, der virkelig er forekomster af Kat. For at gøre dette bruger vi operatøren forekomst af.

4.1. forekomst af Operatør

Vi bruger ofte forekomst af før downcasting for at kontrollere, om objektet tilhører den specifikke type:

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

4.2. ClassCastException

Hvis vi ikke havde kontrolleret typen med forekomst af operatør, ville compileren ikke have klaget. Men ved runtime ville der være en undtagelse.

For at demonstrere dette skal vi fjerne forekomst af operatør fra ovenstående kode:

public void uncheckedFeed (List animals) {animals.forEach (animal -> {animal.eat (); ((Cat) animal) .mow ();}); }

Denne kode kompileres uden problemer. Men hvis vi prøver at køre det, ser vi en undtagelse:

java.lang.ClassCastException: com.baeldung.casting.Dog kan ikke kastes til com.baeldung.casting.Cat

Dette betyder, at vi prøver at konvertere et objekt, der er en forekomst af Hund ind i en Kat eksempel.

ClassCastException 's kastes altid ved kørsel, hvis den type, vi nedskyder til, ikke matcher typen af ​​det rigtige objekt.

Bemærk, at hvis vi prøver at downcaste til en ikke-relateret type, tillader ikke compileren dette:

Dyr; Streng s = (streng) dyr;

Kompilatoren siger “Kan ikke kaste fra dyr til streng”.

For at koden skal kompileres, skal begge typer være i det samme arvetræ.

Lad os opsummere:

  • Downcasting er nødvendigt for at få adgang til medlemmer, der er specifikke for underklassen
  • Downcasting udføres ved hjælp af cast operator
  • Vi har brug for at nedskyde et objekt sikkert forekomst af operatør
  • Hvis det virkelige objekt ikke stemmer overens med den type, vi nedskyder til, så ClassCastException kastes ved kørsel

5. støbt () Metode

Der er en anden måde at kaste genstande ved hjælp af metoderne til Klasse:

offentlig ugyldighed nårDowncastToCatWithCastMethod_thenMeowIsCalled () {Dyredyr = ny kat (); hvis (Cat.class.isInstance (animal)) {Cat cat = Cat.class.cast (animal); cat.meow (); }}

I ovenstående eksempel støbt () og isInstance () metoder anvendes i stedet for støbt og forekomst af operatører tilsvarende.

Det er almindeligt at bruge støbt () og isInstance () metoder med generiske typer.

Lad os skabe AnimalFeederGeneric klasse med foder() metode, der "fodrer" kun en type dyr - katte eller hunde, afhængigt af værdien af ​​typeparameteren:

offentlig klasse AnimalFeederGeneric {privat klasse type; offentlig AnimalFeederGeneric (klassetype) {this.type = type; } offentligt listefeed (liste dyr) {liste liste = ny ArrayList (); animals.forEach (animal -> {if (type.isInstance (animal)) {T objAsType = type.cast (animal); list.add (objAsType);}}); returliste }}

Det foder() metoden kontrollerer hvert dyr og returnerer kun dem, der er forekomster af T.

Bemærk, at Klasse forekomst skal også overføres til den generiske klasse, da vi ikke kan få den fra typeparameteren T. I vores eksempel sender vi det i konstruktøren.

Lad os lave T svarende til Kat og sørg for, at metoden kun returnerer katte:

@Test offentlig ugyldig nårParameterCat_thenOnlyCatsFed () {Liste dyr = ny ArrayList (); animals.add (ny kat ()); animals.add (ny hund ()); AnimalFeederGeneric catFeeder = ny AnimalFeederGeneric (kat.klasse); Liste fedAnimals = catFeeder.feed (dyr); assertTrue (fedAnimals.size () == 1); assertTrue (fedAnimals.get (0) forekomst af kat); }

6. Konklusion

I denne grundlæggende vejledning har vi undersøgt, hvad der er upcasting, downcasting, hvordan man bruger dem, og hvordan disse koncepter kan hjælpe dig med at udnytte polymorfisme.

Som altid er koden til denne artikel tilgængelig på GitHub.