Inheritance is een fundamenteel concept binnen object-oriented programming (OOP) waarbij een klasse (de subclass) eigenschappen en gedrag overneemt van een andere class (de superclass). Hierdoor hoef je bepaalde functionaliteit niet telkens opnieuw te schrijven; je hergebruikt bestaande code in een nieuwe context.
Dit principe maakt het mogelijk om hiërarchieën op te bouwen waarbij de basisfunctionaliteit in de bovenliggende klassen staat en de meer specifieke details in de onderliggende klassen. Denk bijvoorbeeld aan een algemene Dier-klasse die eigenschappen heeft zoals ademhalen() en eten(). Een subclass zoals Hond kan deze functionaliteit erven en uitbreiden met specifieke methodes zoals blaffen().
Voorbeeld:
class Dier:
def ademhalen(self):
print("Ademt")
def eten(self):
print("Eet voedsel")
class Hond(Dier):
def blaffen(self):
print("Blaft")
rex = Hond()
rex.ademhalen() # Uitkomst: Ademt
rex.blaffen() # Uitkomst: Blaft
In bovenstaand voorbeeld zie je dat de Hond-klasse de methodes van Dier erft en daar eigen gedrag aan toevoegt. Dit is het basisidee van inheritance: bestaande functionaliteit gebruiken én uitbreiden.
Inheritance speelt een centrale rol bij het efficiënt organiseren van code in object-oriented programming. Het helpt bij het schrijven van onderhoudbare, schaalbare en herbruikbare software. Hieronder lees je waarom inheritance zo waardevol is in de praktijk.
Wanneer meerdere klassen dezelfde eigenschappen of methodes nodig hebben, kun je deze in een superclass zetten. Subclasses nemen deze functionaliteit over zonder dat je alles opnieuw hoeft te schrijven. Dit voorkomt dubbele code en verkleint de kans op fouten.
Doordat gedeelde functionaliteit op één plek staat, hoef je wijzigingen alleen daar door te voeren. Stel dat je iets update, dan profiteren alle subclasses direct mee. Dit maakt het onderhoud overzichtelijk en consistent.
Inheritance maakt het makkelijk om bestaande klassen uit te breiden zonder hun interne werking aan te passen. Je kunt nieuwe subclasses maken die zich anders gedragen of extra functies hebben, terwijl je de basis (superclass) ongemoeid laat.
Polymorfisme stelt je in staat om objecten van verschillende subclasses als objecten van de superclass te behandelen. Hierdoor kun je generieke functies schrijven die werken met meerdere objecttypes, zolang ze dezelfde interface delen. Dit draagt bij aan flexibele en schaalbare software.
Er bestaan verschillende vormen van inheritance, afhankelijk van hoe de klassenstructuur is opgebouwd. De keuze voor een bepaalde vorm hangt af van de complexiteit van je project en de gewenste herbruikbaarheid van code. Hieronder bespreken we de belangrijkste types inheritance.
Bij single inheritance erft een subclass van één enkele superclass. Dit is de meest voorkomende en eenvoudigste vorm.
class Mens:
def praten(self):
print("Praat")
class Student(Mens):
def studeren(self):
print("Studeren")
s = Student()
s.praten() # Uitkomst: Praat
In dit geval erft een subclass van meerdere superclasses. Niet alle programmeertalen ondersteunen dit vanwege de complexiteit (zoals de "diamond problem").
class Vervoermiddel:
def verplaatsen(self):
print("Beweegt zich voort")
class Elektrisch:
def opladen(self):
print("Laadt op")
class EStep(Vervoermiddel, Elektrisch):
pass
Hierbij is er sprake van een keten van inheritance. Een klasse erft van een subclass, die op zijn beurt weer erft van een andere superclass.
class Dier:
pass
class Zoogdier(Dier):
pass
class Kat(Zoogdier):
pass
Bij deze structuur erven meerdere subclasses van dezelfde superclass. Dit zorgt voor een boomstructuur.
class Apparaat:
pass
class Laptop(Apparaat):
pass
class Telefoon(Apparaat):
pass
Hybrid inheritance is een combinatie van twee of meer bovenstaande types. Het kan complex zijn en leidt in sommige talen tot ambiguïteit.
Bijvoorbeeld: een combinatie van multiple en multilevel inheritance in één structuur.
In inheritance vormen subclasses en superclasses de basis van de hiërarchische structuur. Een superclass (ook wel base class of parent class genoemd) bevat de gedeelde eigenschappen en methodes. Een subclass (ook wel child class) breidt deze uit of past ze aan.
Wanneer een subclass wordt gemaakt, erft deze alle publieke en beschermde (protected) eigenschappen en methodes van de superclass. De subclass kan:
nieuwe methodes toevoegen,
bestaande methodes overschrijven (overriden),
of beide.
Wanneer een subclass wordt geïnstantieerd, wordt eerst de constructor van de superclass aangeroepen, tenzij je dit expliciet overschrijft. Dit gebeurt bijvoorbeeld in Python met super().__init__() of in Java met super().
class Persoon:
def __init__(self, naam):
self.naam = naam
class Werknemer(Persoon):
def __init__(self, naam, functie):
super().__init__(naam)
self.functie = functie
Hieronder bespreken we een aantal bijzondere gevallen:
Sommige klassen mogen niet geërfd worden. Dit wordt vaak aangeduid met het final keyword (bijvoorbeeld in Java of Swift). Dit is handig als je wil voorkomen dat een klasse wordt aangepast of uitgebreid.
Net als bij klassen kunnen methodes als final of sealed worden gemarkeerd, zodat ze niet in de subclass aangepast kunnen worden. Dit verhoogt de veiligheid van je code en voorkomt onverwacht gedrag.
Een virtual method is een methode die kan worden overschreven in een subclass. In sommige talen zoals C# gebruik je hiervoor expliciet het virtual keyword in de superclass en override in de subclass.
In inheritance speelt zichtbaarheid van klasseleden een belangrijke rol. Niet elke eigenschap of methode van de superclass is automatisch toegankelijk vanuit de subclass. Programmeertalen gebruiken access modifiers om dit te regelen. Deze modifiers bepalen wie toegang heeft tot welke delen van een klasse.
De drie meest gebruikte toegangsniveaus zijn:
Een public member is overal toegankelijk — binnen dezelfde klasse, in subclasses en van buitenaf. Dit is de meest open vorm van toegang.
class Persoon:
def __init__(self):
self.naam = "Jan" # public by default in Python
Een protected member is herkenbaar aan een underscore in Python (bijv. _functie) of met het keyword protected in talen als Java of C#. Deze zijn toegankelijk in de klasse zelf én in subclasses, maar niet van buitenaf.
class Persoon:
def __init__(self):
self._functie = "Developer"
Private members zijn alleen beschikbaar binnen de eigen klasse. In Python gebruik je hiervoor een dubbele underscore (__). Andere talen gebruiken het keyword private.
class Persoon:
def __init__(self):
self.__salaris = 5000 # Niet direct toegankelijk in subclass
Toegangsniveaus beschermen interne logica, voorkomen verkeerd gebruik van data en ondersteunen encapsulation — een ander belangrijk principe van OOP.
Inheritance stelt je in staat om methodes van een superclass te hergebruiken, maar soms wil je het gedrag van zo’n methode aanpassen. Dit doe je met overriding. In combinatie met polymorfisme maakt dit object-georiënteerde systemen krachtig en flexibel.
Bij method overriding herdefinieer je in een subclass een methode die al bestaat in de superclass. De nieuwe versie ‘overschrijft’ de oorspronkelijke implementatie.
class Dier:
def geluid(self):
print("Maakt geluid")
class Kat(Dier):
def geluid(self):
print("Miauw")
huisdier = Kat()
huisdier.geluid() # Uitkomst: Miauw
Hier zie je dat Kat de geluid()-methode overschrijft. Bij het aanroepen wordt de versie van de subclass gebruikt, niet die van de superclass.
Overriding is iets anders dan overloading. Overloading betekent dat je meerdere methodes met dezelfde naam maar andere parameters gebruikt. Dit komt vaker voor in statisch getypeerde talen zoals Java en C#, maar niet in Python.
Polymorfisme betekent dat objecten van verschillende subclasses zich op dezelfde manier kunnen gedragen via een gedeelde interface. Je werkt dan met objecten op basis van hun superclass, terwijl hun specifieke gedrag afkomstig is van de subclass.
def maak_geluid(dier: Dier):
dier.geluid()
maak_geluid(Kat()) # Uitkomst: Miauw
maak_geluid(Hond()) # Uitkomst: Blaft
Zolang een object de juiste methode bevat, kan het via polymorfisme worden gebruikt — zonder dat je de exacte subclass hoeft te kennen.
Inheritance is geen doel op zich, maar een middel om je code slimmer, schoner en beter uitbreidbaar te maken. In de praktijk wordt inheritance vaak ingezet om structuur en hergebruik te realiseren in grotere systemen.
Eén van de bekendste toepassingen is code reuse. Door gedeelde functionaliteit in een superclass te plaatsen, hoef je deze niet steeds opnieuw te schrijven in elke subclass. Dit sluit aan bij het DRY-principe (Don't Repeat Yourself), dat overbodige herhaling van code tegengaat.
Voorbeeld: Stel je hebt meerdere soorten gebruikers in een systeem — Beheerder, Editor, Bezoeker. In plaats van dat elke klasse zelf een login()- of logout()-methode heeft, definieer je die één keer in een Gebruiker-superclass.
Inheritance helpt bij het scheiden van generieke en specifieke logica. Je kunt een algemene klasse maken met basisfunctionaliteit en subclasses creëren die zich richten op specifieke rollen, apparaten of gedragingen.
Bijvoorbeeld:
class Bericht:
def verzenden(self):
print("Verzenden...")
class Email(Bericht):
def verzenden(self):
print("E-mail verzonden")
class SMS(Bericht):
def verzenden(self):
print("SMS verzonden")
Je kunt nu op uniforme wijze berichten verzenden, ongeacht het type.
Zoals eerder uitgelegd, maakt inheritance polymorfisme mogelijk. Dit is vooral handig bij het schrijven van algemene functies, zoals een verwerkingssysteem dat werkt met meerdere soorten documenten, producten of gebruikers, zolang ze allemaal afgeleid zijn van dezelfde superclass.
Inheritance en subtyping lijken sterk op elkaar, maar zijn niet hetzelfde. Beide concepten spelen een rol in object-oriented programming, vooral bij polymorfisme en herbruikbaarheid, maar het is belangrijk om het verschil te begrijpen.
Subtyping betekent dat een object van een subclass overal kan worden gebruikt waar een object van de superclass verwacht wordt. Dit heet ook wel het Liskov Substitution Principle (LSP). De subclass moet zich dan gedragen als een soort vervanger van de superclass, zonder onverwacht gedrag te introduceren.
class Vogel:
def vliegen(self):
pass
class Mus(Vogel):
def vliegen(self):
print("De mus vliegt weg")
def laat_vliegen(vogel: Vogel):
vogel.vliegen()
Hier kan Mus zonder problemen worden gebruikt waar Vogel verwacht wordt — een geldig voorbeeld van subtyping.
Inheritance is de techniek waarmee een klasse eigenschappen en methodes erft. Subtyping is de relatie waarbij een subclass zich gedraagt als een bruikbare vervanger van zijn superclass.
Niet elke toepassing van inheritance leidt automatisch tot goed subtyping. Als een subclass gedrag verandert op een manier die de verwachtingen van de superclass doorbreekt, heb je wel inheritance, maar géén geldige subtype-relatie.
Inheritance lijkt soms handig, maar het verkeerd inzetten ervan kan je ontwerp juist kwetsbaar maken:
Tight coupling: Subclasses zijn sterk afhankelijk van de implementatie van de superclass.
Breekbaar ontwerp: Wijzigingen in de superclass kunnen onverwachte bugs veroorzaken in subclasses.
Misbruik van inheritance: Soms wordt inheritance gebruikt puur om code te hergebruiken, terwijl composition of interfaces een betere oplossing zouden zijn.
Daarom luidt een veelgehoord advies: "Favor composition over inheritance." Gebruik inheritance alleen als het semantisch klopt — als een subclass echt is-a variant is van de superclass.
Hoewel inheritance krachtig is, kent het ook nadelen. In grotere projecten kan verkeerd gebruik leiden tot inflexibele en moeilijk te onderhouden code. Daarom is het belangrijk om de beperkingen te kennen én te weten welke alternatieven je hebt.
Subclasses zijn direct afhankelijk van de implementatie van hun superclass. Als je iets verandert in de superclass, kan dit onverwachte gevolgen hebben voor alle subclasses. Dit beperkt je flexibiliteit.
Eenmaal gekozen voor een inheritance-structuur, is het lastig om deze later aan te passen. Grote inheritance-ketens zorgen voor complexiteit maakt debugging lastiger.
Soms erven subclasses methodes die ze eigenlijk niet nodig hebben. Dit zorgt voor onduidelijkheid en onnodige verantwoordelijkheid.
Als je een stuk functionaliteit wilt hergebruiken in meerdere klassen die geen gemeenschappelijke superclass hebben, zit je vast. Inheritance werkt namelijk alleen binnen één hiërarchie.
In plaats van een subclass te maken, kun je ook objecten combineren. Bij composition bevat een object andere objecten met specifieke verantwoordelijkheden. Dit maakt je code flexibeler en beter testbaar.
class Motor:
def starten(self):
print("Motor gestart")
class Auto:
def __init__(self):
self.motor = Motor()
def rijden(self):
self.motor.starten()
print("Auto rijdt")
Sommige talen bieden interfaces of abstracte klassen. Hiermee kun je de structuur en verplichtingen van klassen vastleggen zonder volledige implementatie te delen. Subclasses zijn dan verplicht bepaalde methodes te implementeren, maar zonder dat ze ongewenste code erven.
In talen zoals Python kun je mixins gebruiken: kleine klassen met herbruikbare functionaliteit die je kunt toevoegen aan meerdere klassen, zonder een diepe hiërarchie te creëren.
Inheritance is een krachtig mechanisme binnen object-oriented programming waarmee je gedrag en eigenschappen van één klasse kunt hergebruiken in een andere. Het maakt code onderhoudbaarder, ondersteunt polymorfisme en helpt bij het structureren van grotere systemen.
Toch is het geen oplossing voor alles. Verkeerd gebruik van inheritance kan leiden tot complexe afhankelijkheden en inflexibele structuren. Daarom is het belangrijk om inheritance bewust en spaarzaam in te zetten. Overweeg alternatieven zoals composition, interfaces of mixins wanneer dat logischer of veiliger is.
Kortom: gebruik inheritance wanneer het past binnen een duidelijke is-a relatie, en kies voor andere technieken als je vooral gedrag wilt hergebruiken of losser wilt koppelen.
Inheritance is een principe binnen object-oriented programming waarbij een subclass eigenschappen en gedrag overneemt van een andere klasse, de superclass. Hierdoor kun je code hergebruiken en structureren. Stel je voor: je hebt een klasse Dier met de methode ademhalen(), en een klasse Hond die hiervan erft. Hond kan dan automatisch ook ademhalen() uitvoeren, zonder dat je die methode opnieuw hoeft te definiëren.
De meest bekende vormen van inheritance zijn single inheritance, multiple inheritance, multilevel inheritance en hierarchical inheritance. Elke vorm bepaalt op een andere manier hoe klassen van elkaar erven. In sommige gevallen worden combinaties van deze structuren toegepast, wat men hybrid inheritance noemt. De precieze ondersteuning hiervoor verschilt per programmeertaal.
Inheritance zorgt ervoor dat een subclass automatisch toegang krijgt tot eigenschappen en methodes van een superclass. Polymorfisme maakt het mogelijk dat verschillende objecten zich op dezelfde manier kunnen gedragen via gedeelde methoden. Dit betekent dat je bijvoorbeeld één functie kunt schrijven die meerdere objecttypes accepteert, zolang ze een bepaalde methode bevatten — met als resultaat flexibel en uitbreidbaar gedrag binnen je applicatie.