"Amigo, gillar du valar?"
"Valar? Nej, aldrig hört talas om dem."
"Den är som en ko, bara större och den simmar. För övrigt kom valar från kor. Eh, eller åtminstone delar de en gemensam förfader. Det spelar ingen roll."
"Lyssna upp. Jag vill berätta om ett annat mycket kraftfullt verktyg för OOP: polymorfism . Det har fyra funktioner."
1) Åsidosättande av metod.
Föreställ dig att du har skrivit en "ko"-klass för ett spel. Den har många medlemsvariabler och metoder. Objekt i den här klassen kan göra olika saker: gå, äta, sova. Kor ringer också i en klocka när de går. Låt oss säga att du har implementerat allt i klassen in i minsta detalj.
Så plötsligt säger kunden att han vill släppa en ny nivå av spelet, där alla handlingar sker i havet, och huvudpersonen är en val.
Du började designa Whale-klassen och inser att den bara skiljer sig något från Cow-klassen. Båda klasserna använder mycket liknande logik, och du bestämmer dig för att använda arv.
Cow-klassen är idealisk för att vara förälderklassen: den har redan alla nödvändiga variabler och metoder. Allt du behöver göra är att lägga till valens förmåga att simma. Men det finns ett problem: din val har ben, horn och en klocka. När allt kommer omkring implementerar Cow-klassen denna funktionalitet. Vad kan du göra?
Metodöverskridande kommer till undsättning. Om vi ärver en metod som inte gör exakt vad vi behöver i vår nya klass kan vi ersätta metoden med en annan.
Hur görs detta? I vår descendant-klass deklarerar vi metoden som vi vill ändra (med samma metodsignatur som i den överordnade klassen) . Sedan skriver vi ny kod för metoden. Det är allt. Det är som om föräldraklassens gamla metod inte existerar.
Så här fungerar det:
Koda | Beskrivning |
---|---|
|
Här definierar vi två klasser: Cow och Whale . Whale ärver Cow .
Klassen |
|
Den här koden visar " Jag är en ko " på skärmen. |
|
Den här koden visar " Jag är en val " på skärmen |
Efter att den ärver Cow
och åsidosätter printName
, har klassen Whale
faktiskt följande data och metoder:
Koda | Beskrivning |
---|---|
|
Vi vet ingenting om någon gammal metod. |
"Ärligt talat, det var vad jag förväntade mig."
2) Men det är inte allt.
"Anta att Cow
klassen har en printAll
metod , som anropar de två andra metoderna. Då skulle koden fungera så här:"
Skärmen kommer att visa:
Jag är vit
Jag är en val
Koda | Beskrivning |
---|---|
|
|
|
Skärmen kommer att visa: Jag är vit Jag är en val |
Observera att när Cow-klassens metod printAll () anropas på ett Whale-objekt, kommer metoden Whale's printName() att användas, inte Cow's.
Det viktiga är inte klassen metoden är skriven i, utan snarare typen (klass) av objektet som metoden anropas på.
"Jag förstår."
"Du kan bara ärva och åsidosätta icke-statiska metoder. Statiska metoder ärvs inte och kan därför inte åsidosättas."
Så här ser Whale-klassen ut när vi tillämpar arv och åsidosätter metoderna:
Koda | Beskrivning |
---|---|
|
Så här ser Whale-klassen ut efter att vi tillämpat arv och åsidosatt metoden. Vi vet ingenting om någon gammal printName metod. |
3) Typ gjutning.
Här är en ännu mer intressant punkt. Eftersom en klass ärver alla metoder och data från sin överordnade klass, kan ett objekt i den här klassen refereras av variabler från förälderklassen ( och föräldern till föräldern, etc., ända upp till klassen Object). Tänk på det här exemplet:
Koda | Beskrivning |
---|---|
|
Skärmen kommer att visa: Jag är vit. |
|
Skärmen kommer att visa: Jag är vit. |
|
Skärmen kommer att visa: Whale@da435a. Metoden toString() ärvs från klassen Object. |
"Bra grejer. Men varför skulle du behöva det här?"
"Det är en värdefull egenskap. Du kommer att förstå senare att den är väldigt, väldigt värdefull."
4) Sen bindning (dynamisk utskick).
Så här ser det ut:
Koda | Beskrivning |
---|---|
|
Skärmen kommer att visa: Jag är en val. |
|
Skärmen kommer att visa: Jag är en val. |
Observera att det inte är typen av variabel som bestämmer vilken specifik printName- metod vi kallar (den för klassen Cow eller Whale), utan snarare typen av objekt som variabeln refererar till.
Cow - variabeln lagrar en referens till ett Whale- objekt, och metoden printName som definieras i Whale -klassen kommer att anropas.
"Nja, det har de inte lagt till för tydlighetens skull."
"Ja, det är inte så självklart. Kom ihåg denna viktiga regel:"
Uppsättningen metoder du kan anropa en variabel bestäms av variabelns typ. Men vilken specifik metod/implementering som anropas bestäms av typen/klassen för objektet som variabeln refererar till.
"Jag ska försöka."
"Du kommer att stöta på det här hela tiden, så du kommer snabbt att förstå det och aldrig glömma."
5) Typ gjutning.
Gjutning fungerar annorlunda för referenstyper, dvs klasser, än för primitiva typer. Breddande och avsmalnande omvandlingar gäller dock även referenstyper. Tänk på det här exemplet:
Breddande konvertering | Beskrivning |
---|---|
|
En klassisk breddningsomvandling. Nu kan du bara anropa metoder definierade i Cow-klassen på Whale-objektet. Kompilatorn låter dig endast använda cow-variabeln för att anropa de metoder som definieras av Cow-typen. |
Begränsande konvertering | Beskrivning |
---|---|
|
En klassisk avsmalningskonvertering med typkontroll. Kovariabeln av typen Cow lagrar en referens till ett Whale-objekt. Vi kontrollerar att så är fallet och utför sedan (vidgnings-) typkonverteringen. Detta kallas även typgjutning . |
|
Du kan också utföra en avsmalnande konvertering av en referenstyp utan att typkontrollera objektet. I det här fallet, om kovariabeln pekar på något annat än ett Whale-objekt, kommer ett undantag (InvalidClassCastException) att kastas. |
6) Och nu till något gott. Kallar den ursprungliga metoden.
Ibland när du åsidosätter en ärvd metod vill du inte helt ersätta den. Ibland vill man bara lägga till lite till det.
I det här fallet vill du verkligen att den nya metodens kod ska anropa samma metod, men på basklassen. Och Java låter dig göra detta. Så här går det till: super.method()
.
Här är några exempel:
Koda | Beskrivning |
---|---|
|
|
|
Skärmen kommer att visa: Jag är vit Detta är falskt: Jag är en ko Jag är en val |
"Hmm. Tja, det var en lektion. Mina robotöron smälte nästan."
"Ja, det här är inte enkla saker. Det är något av det svåraste materialet du kommer att stöta på. Professorn lovade att ge länkar till material från andra författare, så att om du fortfarande inte förstår något kan du fylla i luckor."