"Amigo, hou je van walvissen?"
"Walvissen? Nee, nog nooit van gehoord."
"Het is net een koe, alleen groter en hij zwemt. Trouwens, walvissen zijn voortgekomen uit koeien. Uh, of ze hebben tenminste een gemeenschappelijke voorouder. Het maakt niet uit."
"Luister. Ik wil je vertellen over een andere zeer krachtige tool van OOP: polymorfisme . Het heeft vier kenmerken."
1) Overheersende methode.
Stel je voor dat je een "Koe"-klasse hebt geschreven voor een spel. Het heeft veel lidvariabelen en methoden. Objecten van deze klasse kunnen verschillende dingen doen: lopen, eten, slapen. Koeien laten ook een belletje rinkelen als ze lopen. Laten we zeggen dat je alles in de klas tot in het kleinste detail hebt geïmplementeerd.
Dan zegt de klant plotseling dat hij een nieuw niveau van het spel wil uitbrengen, waar alle acties in de zee plaatsvinden en de hoofdpersoon een walvis is.
Je begon de Whale-klasse te ontwerpen en besefte dat deze slechts een klein beetje verschilt van de Cow-klasse. Beide klassen gebruiken vergelijkbare logica en u besluit overerving te gebruiken.
De klasse Cow is bij uitstek geschikt om de ouderklasse te zijn: deze beschikt al over alle benodigde variabelen en methoden. Het enige dat u hoeft te doen, is het vermogen van de walvis om te zwemmen toe te voegen. Maar er is een probleem: je walvis heeft poten, hoorns en een bel. De klasse Cow implementeert deze functionaliteit immers. Wat kan je doen?
Methode negeren komt te hulp. Als we een methode erven die niet precies doet wat we nodig hebben in onze nieuwe klasse, kunnen we de methode vervangen door een andere.
Hoe wordt dit gedaan? In onze afstammelingenklasse declareren we de methode die we willen wijzigen (met dezelfde methodehandtekening als in de bovenliggende klasse) . Vervolgens schrijven we nieuwe code voor de methode. Dat is het. Het is alsof de oude methode van de bovenliggende klasse niet bestaat.
Dit is hoe het werkt:
Code | Beschrijving |
---|---|
|
Hier definiëren we twee klassen: Cow en Whale . Whale erft Cow .
De |
|
Deze code toont « Ik ben een koe » op het scherm. |
|
Deze code toont « Ik ben een walvis » op het scherm |
Nadat het erft Cow
en overschrijft printName
, Whale
heeft de klasse eigenlijk de volgende gegevens en methoden:
Code | Beschrijving |
---|---|
|
We weten niets over een oude methode. |
"Eerlijk gezegd, dat was wat ik verwachtte."
2) Maar dat is niet alles.
"Stel dat de Cow
klasse een printAll
methode heeft die de twee andere methoden aanroept. Dan zou de code als volgt werken:"
Op het scherm verschijnt:
ik ben wit,
ik ben een walvis
Code | Beschrijving |
---|---|
|
|
|
Op het scherm verschijnt: ik ben wit, ik ben een walvis |
Merk op dat wanneer de methode printAll() van de klasse Cow wordt aangeroepen voor een Whale-object, de methode printName() van de Whale wordt gebruikt, niet die van Cow.
Het belangrijkste is niet de klasse waarin de methode is geschreven, maar het type (klasse) van het object waarop de methode wordt aangeroepen.
"Ik zie."
"U kunt alleen niet-statische methoden overnemen en overschrijven. Statische methoden worden niet overgeërfd en kunnen daarom niet worden overschreven."
Zo ziet de klasse Whale eruit nadat we overerving hebben toegepast en de methoden hebben overschreven:
Code | Beschrijving |
---|---|
|
Zo ziet de klasse Whale eruit nadat we overerving hebben toegepast en de methode hebben overschreven. We weten niets over een oude printName methode. |
3) Type gieten.
Hier is een nog interessanter punt. Omdat een klasse alle methoden en gegevens van zijn bovenliggende klasse overerft, kan naar een object van deze klasse worden verwezen door variabelen van de bovenliggende klasse (en de bovenliggende klasse van de bovenliggende klasse, enz., tot en met de klasse Object). Overweeg dit voorbeeld:
Code | Beschrijving |
---|---|
|
Op het scherm verschijnt: Ik ben blank. |
|
Op het scherm verschijnt: Ik ben blank. |
|
Op het scherm verschijnt: Whale@da435a. De methode toString() wordt geërfd van de klasse Object. |
"Goed spul. Maar waarom zou je dit nodig hebben?"
"Het is een waardevol kenmerk. Je zult later begrijpen dat het heel, heel waardevol is."
4) Late binding (dynamische verzending).
Hier is hoe het eruit ziet:
Code | Beschrijving |
---|---|
|
Op het scherm verschijnt: Ik ben een walvis. |
|
Op het scherm verschijnt: Ik ben een walvis. |
Merk op dat niet het type variabele bepaalt welke specifieke methode printName we aanroepen (die van de klasse Cow of Whale), maar eerder het type object waarnaar door de variabele wordt verwezen.
De variabele Cow slaat een verwijzing naar een Whale- object op en de methode printName die in de Whale- klasse is gedefinieerd, wordt aangeroepen.
"Nou, dat hebben ze voor de duidelijkheid niet toegevoegd."
"Ja, het is niet zo voor de hand liggend. Onthoud deze belangrijke regel:"
De set methoden die u op een variabele kunt aanroepen, wordt bepaald door het type van de variabele. Maar welke specifieke methode/implementatie wordt aangeroepen, wordt bepaald door het type/de klasse van het object waarnaar wordt verwezen door de variabele.
"Ik zal het proberen."
"Je zult dit constant tegenkomen, dus je zult het snel begrijpen en nooit meer vergeten."
5) Type gieten.
Casten werkt anders voor referentietypen, dwz klassen, dan voor primitieve typen. Verbredings- en versmallingsconversies zijn echter ook van toepassing op referentietypen. Overweeg dit voorbeeld:
Verbreding conversie | Beschrijving |
---|---|
|
Een klassieke verbredende conversie. Nu kunt u alleen methoden aanroepen die zijn gedefinieerd in de klasse Cow op het Whale-object. De compiler laat u de variabele koe alleen gebruiken om de methoden aan te roepen die zijn gedefinieerd door het type Koe. |
Vernauwende conversie | Beschrijving |
---|---|
|
Een klassieke versmallende conversie met een typecontrole. De variabele cow van het type Cow slaat een verwijzing op naar een Whale-object. We controleren of dit het geval is en voeren vervolgens de (verbredende) typeconversie uit. Dit wordt ook wel typecasting genoemd . |
|
U kunt ook een vernauwende conversie van een referentietype uitvoeren zonder typecontrole van het object. In dit geval, als de cow- variabele naar iets anders dan een Whale-object wijst, wordt er een uitzondering (InvalidClassCastException) gegenereerd. |
6) En nu iets lekkers. De oorspronkelijke methode aanroepen.
Wanneer u een overgeërfde methode overschrijft, wilt u deze soms niet volledig vervangen. Soms wil je er gewoon een klein beetje aan toevoegen.
In dit geval wilt u echt dat de code van de nieuwe methode dezelfde methode aanroept, maar dan in de basisklasse. En Java laat je dit doen. Zo werkt het: super.method()
.
Hier zijn enkele voorbeelden:
Code | Beschrijving |
---|---|
|
|
|
Op het scherm verschijnt: Ik ben wit. Dit is onjuist: ik ben een koe, ik ben een walvis |
"Hmm. Nou, dat was een lesje. Mijn robotoren smolten bijna."
"Ja, dit zijn geen simpele dingen. Het is een van de moeilijkste materialen die je zult tegenkomen. De professor beloofde links te geven naar materialen van andere auteurs, zodat je, als je nog steeds iets niet begrijpt, de hiaten."
GO TO FULL VERSION