"Amigo, kan du lide hvaler?"

"Hvaler? Nej, aldrig hørt om dem."

"Den er ligesom en ko, kun større og den svømmer. I øvrigt kom hvaler fra køer. Øh, eller i det mindste deler de en fælles forfader. Det er lige meget."

Polymorfi og tilsidesættelse - 1

"Lyt op. Jeg vil gerne fortælle dig om et andet meget kraftfuldt OOP-værktøj: polymorfisme . Det har fire funktioner."

1) Metode tilsidesættelse.

Forestil dig, at du har skrevet en "ko"-klasse til et spil. Den har masser af medlemsvariabler og -metoder. Objekter i denne klasse kan gøre forskellige ting: gå, spise, sove. Køer ringer også med en klokke, når de går. Lad os sige, at du har implementeret alt i klassen ned til mindste detalje.

Polymorfi og tilsidesættelse - 2

Så pludselig siger kunden, at han vil frigive et nyt niveau af spillet, hvor alle handlinger foregår i havet, og hovedpersonen er en hval.

Du begyndte at designe Whale-klassen og indse, at den kun er lidt anderledes end Cow-klassen. Begge klasser bruger meget ens logik, og du beslutter dig for at bruge arv.

Ko-klassen er ideel til at være forældreklassen: den har allerede alle de nødvendige variabler og metoder. Alt du skal gøre er at tilføje hvalens evne til at svømme. Men der er et problem: din hval har ben, horn og en klokke. Når alt kommer til alt, implementerer Cow-klassen denne funktionalitet. Hvad kan du gøre?

Polymorfi og tilsidesættelse - 3

Metodetilsidesættelse kommer til undsætning. Hvis vi arver en metode, der ikke gør præcis, hvad vi har brug for i vores nye klasse, kan vi erstatte metoden med en anden.

Polymorfi og tilsidesættelse - 4

Hvordan gøres dette? I vores efterkommerklasse erklærer vi den metode, som vi ønsker at ændre (med samme metodesignatur som i den overordnede klasse) . Så skriver vi ny kode til metoden. Det er det. Det er som om forældreklassens gamle metode ikke eksisterer.

Sådan 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 tilsidesætter  printName();metoden.

public static void main(String[] args)
{
Cow cow = new Cow();
cow.printName();
}
Denne kode viser " Jeg er en ko " på skærmen.
public static void main(String[] args)
{
Whale whale = new Whale();
whale.printName();
}
Denne kode viser " Jeg er en hval " på skærmen

Efter at den har arvet Cowog tilsidesat 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 ved intet om nogen gammel metode.

"Helt ærligt, det var det, jeg havde forventet."

2) Men det er ikke alt.

"Antag at  Cow klassen har en  printAll, metode, der kalder de to andre metoder. Så ville koden fungere sådan her:"

Skærmen vil vise:
Jeg er hvid,
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();
}
Skærmen vil vise:
Jeg er hvid,
jeg er en hval

Bemærk, at når Cow-klassens printAll ()-metode kaldes på et Whale-objekt, vil Whale's printName()-metoden blive brugt, ikke Cow's.

Det vigtige er ikke klassen metoden er skrevet i, men snarere typen (klasse) af det objekt, som metoden kaldes på.

"Jeg ser."

"Du kan kun arve og tilsidesætte ikke-statiske metoder. Statiske metoder nedarves ikke og kan derfor ikke tilsidesættes."

Sådan ser Whale-klassen ud, efter at vi har anvendt arv og tilsidesat metoderne:

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");
}
}
Sådan ser Whale-klassen ud, efter at vi har anvendt arv og tilsidesat metoden. Vi ved intet om nogen gammel printNamemetode.

3) Type støbning.

Her er et endnu mere interessant punkt. Fordi en klasse arver alle metoderne og dataene fra dens overordnede klasse, kan et objekt i denne klasse refereres af variabler fra den overordnede klasse (og forælderen til forælderen osv., lige op til Object-klassen). Overvej dette eksempel:

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

"Gode ting. Men hvorfor skulle du bruge det her?"

"Det er en værdifuld funktion. Du vil senere forstå, at den er meget, meget værdifuld."

4) Sen binding (dynamisk afsendelse).

Sådan ser det ud:

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

Bemærk, at det ikke er typen af ​​variabel, der bestemmer, hvilken specifik printName- metode vi kalder (den af ​​Cow- eller Whale-klassen), men derimod typen af ​​objekt, der refereres til af variablen.

Cow - variablen gemmer en reference til et Whale- objekt, og printName- metoden defineret i Whale- klassen vil blive kaldt.

"Jamen, det tilføjede de ikke for overskuelighedens skyld."

"Ja, det er ikke så indlysende. Husk denne vigtige regel:"

Sættet af metoder, du kan kalde på en variabel, bestemmes af variablens type. Men hvilken specifik metode/implementering der bliver kaldt, bestemmes af typen/klassen af ​​objektet, som variablen refererer til.

"Jeg vil gøre et forsøg."

"Du vil løbe ind i det her konstant, så du vil hurtigt forstå det og aldrig glemme."

5) Type støbning.

Støbning fungerer anderledes for referencetyper, altså klasser, end det gør for primitive typer. Udvidelse og indsnævring af konverteringer gælder dog også for referencetyper. Overvej dette eksempel:

Udvidelseskonvertering Beskrivelse
Cow cow = new Whale();

En klassisk udvidelseskonvertering. Nu kan du kun kalde metoder defineret i Cow-klassen på Whale-objektet.

Compileren vil kun lade dig bruge ko-variablen til at kalde de metoder, der er defineret af ko-typen.

Indsnævring af konvertering Beskrivelse
Cow cow = new Whale();
if (cow instanceof Whale)
{
Whale whale = (Whale) cow;
}
En klassisk indsnævringskonvertering med typetjek. Kovariablen af ​​typen Cow gemmer en reference til et hvalobjekt.
Vi kontrollerer, at dette er tilfældet , og udfører derefter (udvidelses-) typekonverteringen. Dette kaldes også typestøbning .
Cow cow = new Cow();
Whale whale = (Whale) cow; //exception
Du kan også udføre en indsnævrende konvertering af en referencetype uden at typetjekke objektet.
I dette tilfælde, hvis ko- variablen peger på noget andet end et Whale-objekt, vil en undtagelse (InvalidClassCastException) blive kastet.

6) Og nu til noget velsmagende. Kalder den oprindelige metode.

Nogle gange, når du tilsidesætter en nedarvet metode, ønsker du ikke helt at erstatte den. Nogle gange vil man bare tilføje en lille smule til det.

I dette tilfælde ønsker du virkelig, at den nye metodes kode skal kalde den samme metode, men på basisklassen. Og Java lader dig gøre dette. Sådan gøres det:  super.method().

Her er nogle 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();
}
Skærmen vil vise:
Jeg er hvid
Dette er falsk: Jeg er en ko,
jeg er en hval

"Hmm. Nå, det var en lektie. Mine robotører smeltede næsten."

"Ja, det er ikke simple ting. Det er noget af det sværeste materiale, du vil støde på. Professoren lovede at give links til materialer fra andre forfattere, så hvis du stadig ikke forstår noget, kan du udfylde huller."