Overerving
In de voorgaande lessen heb je overerving meerdere keren gezien. In de Java-taal kunnen klassen worden afgeleid van andere klassen, waardoor velden en methoden van die klassen worden overgeërfd.
Met uitzondering van
Object
, die geen superklasse heeft, heeft elke klasse een en slechts één directe superklasse (enkele overerving). Bij afwezigheid van een andere expliciete superklasse is elke klasse impliciet een subklasse van Object
. Klassen kunnen worden afgeleid van klassen die zijn afgeleid van klassen die zijn afgeleid van klassen, en enzovoort, en uiteindelijk afgeleid van de bovenste klasse,
Object
. Zo’n klasse zou afstammen van alle klassen in de overervingsketen die teruggaat tot Object
. Het idee van overerving is eenvoudig maar krachtig: als u een nieuwe klasse wilt maken en er is al een klasse die een deel van de code bevat die u wilt, kunt u uw nieuwe klasse afleiden uit de bestaande klasse . Door dit te doen, kunt u de velden en methoden van de bestaande klasse hergebruiken zonder ze zelf te hoeven schrijven (en debuggen!).
Een subklasse erft alle leden (velden, methoden en geneste klassen) van zijn superklasse. Constructors zijn geen leden, dus ze worden niet geërfd door subklassen, maar de constructor van de superklasse kan worden aangeroepen vanuit de subklasse.
De Java Platform Class Hiërarchie
De Object
class, gedefinieerd in het java.lang
pakket, definieert en implementeert gedrag dat gemeenschappelijk is voor alle klassen, inclusief degene die je schrijft. In het Java-platform zijn veel klassen rechtstreeks afgeleid van Object
, andere klassen zijn afgeleid van een aantal van die klassen, enzovoort, en vormen een hiërarchie van klassen.
Alle klassen in het Java-platform zijn afstammelingen van het object
Bovenaan de hiërarchie is Object
de meest algemene van alle klassen. Klassen onder aan de hiërarchie bieden meer gespecialiseerd gedrag.
Een voorbeeld van overerving
Hier is de voorbeeldcode voor een mogelijke implementatie van een Bicycle
klasse die werd gepresenteerd in de les Klassen en objecten:
Een klassendeclaratie voor een MountainBike
klasse die een subklasse is van Bicycle
zou er zo uit kunnen zien:
public class MountainBike extends Bicycle { // the MountainBike subclass adds one field public int seatHeight; // the MountainBike subclass has one constructor public MountainBike(int startHeight, int startCadence, int startSpeed, int startGear) { super(startCadence, startSpeed, startGear); seatHeight = startHeight; } // the MountainBike subclass adds one method public void setHeight(int newValue) { seatHeight = newValue; } }
MountainBike
erft alle velden en methoden van Bicycle
en voegt het veld seatHeight
toe en een methode om het in te stellen. Met uitzondering van de constructor, is het alsof je een nieuwe MountainBike
klasse helemaal opnieuw hebt geschreven, met vier velden en vijf methoden. Je hoefde echter niet al het werk te doen. Dit zou vooral waardevol zijn als de methoden in de Bicycle
klasse complex waren en veel tijd hadden geduurd om te debuggen.
Wat u kunt doen in een subklasse
Een subklasse erft alle openbare en beschermde leden van de bovenliggende klasse, ongeacht in welk pakket de subklasse zich bevindt. Als de subklasse zich in hetzelfde pakket bevindt als zijn parent, het erft ook de pakket-private leden van de parent. U kunt de overgenomen leden gebruiken zoals ze zijn, ze vervangen, verbergen of aanvullen met nieuwe leden:
- De overgenomen velden kunnen direct worden gebruikt, net als alle andere velden.
- U kunt een veld in de subklasse declareren met dezelfde naam als die in de superklasse, waardoor het wordt verborgen (niet aanbevolen).
- U kunt nieuwe velden in de subklasse declareren die niet in de superklasse zitten.
- De overgenomen methoden kunnen direct worden gebruikt zoals ze zijn.
- U kunt een nieuwe instantiemethode schrijven in de subklasse dat heeft dezelfde handtekening als die in de superklasse, waardoor deze wordt overschreven.
- Je kunt een nieuwe statische methode schrijven in de subklasse die dezelfde handtekening heeft als die in de superklasse, en deze dus verbergen.
- Je kunt nieuwe methoden in de subklasse declareren die niet in de superklasse zitten.
- Je kunt een subklasse constructor schrijven die de constructor van de superklasse aanroept, ofwel impliciet ofwel door het trefwoord
super
.
De volgende secties in deze les zullen uitgebreid worden over deze onderwerpen.
Privéleden in een superklasse
Een subklasse erft de private
leden van de bovenliggende klasse niet. Als de superklasse echter openbare of beschermde methoden heeft om toegang te krijgen tot zijn privévelden, kunnen deze ook door de subklasse worden gebruikt.
Een geneste klasse heeft toegang tot alle privé-leden van de omsluitende klasse – zowel velden als methoden. Daarom heeft een openbare of beschermde geneste klasse die is geërfd door een subklasse, indirecte toegang tot alle privéleden van de superklasse.
Objecten casten
We hebben gezien dat een object van de hoogste klasse is. gegevenstype van de klasse waaruit het werd geïnstantieerd. Als we bijvoorbeeld
public MountainBike myBike = new MountainBike();
schrijven, dan is myBike
van het type MountainBike
.
MountainBike
stamt af van Bicycle
en Object
. Daarom is een MountainBike
een Bicycle
en is het ook een Object
, en het kan ook wordt overal gebruikt waar Bicycle
of Object
objecten nodig zijn.
Het omgekeerde is niet noodzakelijk waar: een Bicycle
mag dan een MountainBike
zijn, maar dat is niet noodzakelijk. Evenzo kan een Object
een Bicycle
of een MountainBike
, maar dat hoeft niet.
Casten toont het gebruik van een object van een typ in plaats van een ander type, onder de objecten die zijn toegestaan door overerving en implementaties. Als we bijvoorbeeld
Object obj = new MountainBike();
schrijven, dan is obj
beide een Object
en een MountainBike
(totdat aan obj
een ander object is toegewezen dat geen MountainBike
). Dit wordt impliciet casten genoemd.
Als we daarentegen
MountainBike myBike = obj;
schrijven, zouden we krijg een compilatietijdfout omdat obj
niet bekend is bij de compiler als een MountainBike
. We kunnen de compiler echter vertellen dat we beloven een MountainBike
toe te wijzen aan obj
door expliciet te casten:
MountainBike myBike = (MountainBike)obj;
Deze cast voegt een runtime-controle in dat obj
een MountainBike
zodat de compiler veilig kan aannemen dat obj
een MountainBike
is. Als obj
geen MountainBike
is tijdens runtime, wordt er een uitzondering gegenereerd.
instanceof
operator. Dit kan u behoeden voor een runtime-fout als gevolg van een onjuiste cast. Bijvoorbeeld:
if (obj instanceof MountainBike) { MountainBike myBike = (MountainBike)obj;}
Hier verifieert de instanceof
operator dat obj
verwijst naar een MountainBike
zodat we de cast kunnen maken met de wetenschap dat er geen runtime-uitzondering wordt gegenereerd.