Tuple Logo
inheritance-in-oop

SHARE

Inheritance

Wat is inheritance?

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.

Waarom inheritance belangrijk is

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.

Vermindert duplicatie van code

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.

Maakt onderhoud eenvoudiger

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.

Verbetert uitbreidbaarheid

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.

Ondersteunt polymorfisme

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.

Soorten inheritance

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.

Single 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 

Multiple inheritance

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

Multilevel inheritance

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

Hierarchical inheritance

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

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.

Subclasses en superclasses

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.

Wat gebeurt er precies bij overerving?

Wanneer een subclass wordt gemaakt, erft deze alle publieke en beschermde (protected) eigenschappen en methodes van de superclass. De subclass kan:

Constructor chaining

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:

Non-subclassable classes

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.

Non-overridable methods

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.

Virtual methods

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.

Toegangsniveaus en zichtbaarheid

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:

Public

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

Protected

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

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.

Overriding en polymorfisme

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.

Wat is overriding?

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.

Verschil met overloading

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.

Wat is polymorfisme?

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.

Toepassingen van inheritance

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.

Code reuse en het DRY-principe

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.

Structuur en logica scheiden

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.

Ondersteuning voor polymorf gedrag

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 vs subtyping

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.

Wat is subtyping?

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.

Het verschil met inheritance

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.

Ontwerpbeperkingen en valkuilen

Inheritance lijkt soms handig, maar het verkeerd inzetten ervan kan je ontwerp juist kwetsbaar maken:

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.

Problemen en alternatieven

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.

Veelvoorkomende problemen

Tight coupling

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.

Rigide hiërarchieën

Eenmaal gekozen voor een inheritance-structuur, is het lastig om deze later aan te passen. Grote inheritance-ketens zorgen voor complexiteit maakt debugging lastiger.

Code-erfenis zonder noodzaak

Soms erven subclasses methodes die ze eigenlijk niet nodig hebben. Dit zorgt voor onduidelijkheid en onnodige verantwoordelijkheid.

Beperkte herbruikbaarheid

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.

Alternatieven voor inheritance

Composition over inheritance

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")

Interfaces of abstracte basisklassen

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.

Mixins

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.

Slim omgaan met inheritance

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.

Veelgestelde vragen
Wat is inheritance met een voorbeeld?

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.


Wat zijn de 4 soorten inheritance?

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.


Wat is inheritance en polymorfisme in programmeren?

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.


Ook interessant

Nieuwsgierig geworden?

Wij vertellen je graag meer!

Contact opnemen
Tuple Logo
Veenendaal (HQ)
De Smalle Zijde 3-05, 3903 LL Veenendaal
info@tuple.nl‭+31 318 24 01 64‬
Snel navigeren
Succesverhalen