"Amigo, liker du hval?"

"Hval? Nei, aldri hørt om dem."

"Den er som en ku, bare større og den svømmer. Hvaler kom forresten fra kuer. Uh, eller i det minste deler de en felles stamfar. Det spiller ingen rolle."

Polymorfisme og overstyring - 1

"Hør her. Jeg vil fortelle deg om et annet veldig kraftig verktøy for OOP: polymorfisme . Det har fire funksjoner."

1) Metodeoverstyring.

Tenk deg at du har skrevet en "Ku"-klasse for et spill. Den har mange medlemsvariabler og metoder. Objekter i denne klassen kan gjøre forskjellige ting: gå, spise, sove. Kyr ringer også i bjelle når de går. La oss si at du har implementert alt i klassen ned til minste detalj.

Polymorfisme og overstyring - 2

Så plutselig sier kunden at han vil slippe et nytt nivå av spillet, der alle handlinger foregår i havet, og hovedpersonen er en hval.

Du begynte å designe Whale-klassen og innse at den bare er litt annerledes enn Cow-klassen. Begge klassene bruker veldig lik logikk, og du bestemmer deg for å bruke arv.

Cow-klassen er ideell til å være foreldreklassen: den har allerede alle nødvendige variabler og metoder. Alt du trenger å gjøre er å legge til hvalens evne til å svømme. Men det er et problem: hvalen din har bein, horn og en bjelle. Tross alt implementerer Cow-klassen denne funksjonaliteten. Hva kan du gjøre?

Polymorfisme og overstyring - 3

Metodeoverstyring kommer til unnsetning. Hvis vi arver en metode som ikke gjør akkurat det vi trenger i vår nye klasse, kan vi erstatte metoden med en annen.

Polymorfisme og overstyring - 4

Hvordan gjøres dette? I vår etterkommerklasse erklærer vi metoden vi ønsker å endre (med samme metodesignatur som i overordnet klasse) . Deretter skriver vi ny kode for metoden. Det er det. Det er som om foreldreklassens gamle metode ikke eksisterer.

Slik fungerer det:

Kode Beskrivelse
class Cow
{
public void printColor()
{
System.out.println("I'm white");
}
public void printName()
{
System.out.println("I'm a cow");
}
}class Whale extends Cow
{
public void printName()
{
System.out.println("I'm a whale");
}
}
Her definerer vi to klasser:  Cow og  WhaleWhalearver  Cow.

Klassen  Whale overstyrer  printName();metoden.

public static void main(String[] args)
{
Cow cow = new Cow();
cow.printName();
}
Denne koden viser « Jeg er en ku » på skjermen.
public static void main(String[] args)
{
Whale whale = new Whale();
whale.printName();
}
Denne koden viser « Jeg er en hval » på skjermen

Etter at den har arvet Cowog overstyrt printName, Whalehar klassen faktisk følgende data og metoder:

Kode Beskrivelse
class Whale
{
public void printColor()
{
System.out.println("I'm white");
}
public void printName()
{
System.out.println("I'm a whale");
}
}
Vi vet ingenting om noen gammel metode.

"Ærlig talt, det var det jeg forventet."

2) Men det er ikke alt.

"Anta at  Cow klassen har en  printAllmetode som kaller de to andre metodene. Da vil koden fungere slik:"

Skjermen vil vise:
Jeg er hvit,
jeg er en hval

Kode Beskrivelse
class Cow
{
public void printAll()
{
printColor();
printName();
}
public void printColor()
{
System.out.println("I'm white");
}
public void printName()
{
System.out.println("I'm a cow");
}
}

class Whale extends Cow
{
public void printName()
{
System.out.println("I'm a whale");
}
}
public static void main(String[] args)
{
Whale whale = new Whale();
whale.printAll();
}
Skjermen vil vise:
Jeg er hvit,
jeg er en hval

Merk at når Cow-klassens printAll ()-metode kalles på et Whale-objekt, vil Whale's printName()-metoden bli brukt, ikke Cow's.

Det viktige er ikke klassen metoden er skrevet i, men snarere type (klasse) av objektet som metoden kalles på.

"Jeg skjønner."

"Du kan bare arve og overstyre ikke-statiske metoder. Statiske metoder er ikke arvet og kan derfor ikke overstyres."

Slik ser Whale-klassen ut etter at vi har brukt arv og overstyrt metodene:

Kode Beskrivelse
class Whale
{
public void printAll()
{
printColor();
printName();
}
public void printColor()
{
System.out.println("I'm white");
}
public void printName()
{
System.out.println("I'm a whale");
}
}
Slik ser hvalklassen ut etter at vi har brukt arv og overstyrt metoden. Vi vet ingenting om noen gammel printNamemetode.

3) Type støping.

Her er et enda mer interessant poeng. Fordi en klasse arver alle metodene og dataene til den overordnede klassen, kan et objekt i denne klassen refereres til av variabler fra den overordnede klassen (og overordnet til overordnet, etc., helt opp til objektklassen). Tenk på dette eksemplet:

Kode Beskrivelse
public static void main(String[] args)
{
Whale whale = new Whale();
whale.printColor();
}
Skjermen vil vise:
Jeg er hvit.
public static void main(String[] args)
{
Cow cow = new Whale();
cow.printColor();
}
Skjermen vil vise:
Jeg er hvit.
public static void main(String[] args)
{
Object o = new Whale();
System.out.println(o.toString());
}
Skjermen vil vise:
Whale@da435a.
Metoden toString() er arvet fra Object-klassen.

"Bra greier. Men hvorfor skulle du trenge dette?"

"Det er en verdifull funksjon. Du vil forstå senere at den er veldig, veldig verdifull."

4) Sen binding (dynamisk utsendelse).

Slik ser det ut:

Kode Beskrivelse
public static void main(String[] args)
{
Whale whale = new Whale();
whale.printName();
}
Skjermen vil vise:
Jeg er en hval.
public static void main(String[] args)
{
Cow cow = new Whale();
cow.printName();
}
Skjermen vil vise:
Jeg er en hval.

Merk at det ikke er typen av variabelen som bestemmer hvilken spesifikk printName- metode vi kaller (den for Cow- eller Whale-klassen), men snarere typen objekt som variabelen refererer til.

Cow - variabelen lagrer en referanse til et Whale- objekt, og printName- metoden definert i Whale- klassen vil bli kalt.

"Vel, det la de ikke til for ordens skyld."

"Ja, det er ikke så åpenbart. Husk denne viktige regelen:"

Settet med metoder du kan kalle på en variabel bestemmes av variabelens type. Men hvilken spesifikk metode/implementering som blir kalt, bestemmes av typen/klassen til objektet som variabelen refererer til.

"Jeg skal prøve."

"Du vil støte på dette hele tiden, så du vil raskt forstå det og aldri glemme."

5) Type støping.

Støping fungerer annerledes for referansetyper, altså klasser, enn det gjør for primitive typer. Utvidende og innsnevrede konverteringer gjelder imidlertid også for referansetyper. Tenk på dette eksemplet:

Utvidende konvertering Beskrivelse
Cow cow = new Whale();

En klassisk utvidelseskonvertering. Nå kan du bare kalle metoder definert i Cow-klassen på Whale-objektet.

Kompilatoren lar deg bare bruke cow-variabelen for å kalle de metodene som er definert av Cow-typen.

Begrensende konvertering Beskrivelse
Cow cow = new Whale();
if (cow instanceof Whale)
{
Whale whale = (Whale) cow;
}
En klassisk innsnevringskonvertering med typesjekk. Cow - variabelen av typen Cow lagrer en referanse til et hvalobjekt.
Vi sjekker at dette er tilfellet , og utfører deretter (utvidende) typekonvertering. Dette kalles også typestøping .
Cow cow = new Cow();
Whale whale = (Whale) cow; //exception
Du kan også utføre en innsnevringskonvertering av en referansetype uten å typesjekke objektet.
I dette tilfellet, hvis cow- variabelen peker på noe annet enn et Whale-objekt, vil et unntak (InvalidClassCastException) bli kastet.

6) Og nå til noe velsmakende. Kaller den opprinnelige metoden.

Noen ganger når du overstyrer en nedarvet metode, vil du ikke helt erstatte den. Noen ganger vil du bare legge til litt.

I dette tilfellet vil du virkelig at den nye metodens kode skal kalle den samme metoden, men på basisklassen. Og Java lar deg gjøre dette. Slik gjøres det:  super.method().

Her er noen eksempler:

Kode Beskrivelse
class Cow
{
public void printAll()
{
printColor();
printName();
}
public void printColor()
{
System.out.println("I'm white");
}
public void printName()
{
System.out.println("I'm a cow");
}
}

class Whale extends Cow
{
public void printName()
{
System.out.print("This is false: ");
super.printName();

System.out.println("I'm a whale");
}
}
public static void main(String[] args)
{
Whale whale = new Whale();
whale.printAll();
}
Skjermen vil vise:
Jeg er hvit
Dette er falskt: Jeg er en ku
jeg er en hval

"Hmm. Vel, det var en leksjon. Robotørene mine smeltet nesten."

"Ja, dette er ikke enkle ting. Det er noe av det vanskeligste materialet du vil møte. Professoren lovet å gi lenker til materiale fra andre forfattere, slik at hvis du fortsatt ikke forstår noe, kan du fylle ut hull."