Begriff
Polymorphism
Warum wichtig?
Dieser Begriff ist ein Knoten im SengakujiWorks-Wissensnetz. Nutze Level 0 für die erste Einordnung, Level 1 für Praxis, Level 2 für technische Struktur und Level 3 für Grenzen, Fallstricke und Expertenkontext.
Das Wort bedeutet "Vielgestaltigkeit".
In Python/Java heißt das:
Eine Funktion verhält sich anders, je nachdem wer sie aufruft.
Du hast eine Liste von Tieren: [Hund, Katze, Kuh].
Du rufst für jedes Tier machGeräusch() auf.
- Der Hund macht "Wuff".
- Die Katze macht "Miau".
Der Code ruft immer dieselbe Methode, aber das Ergebnis ist individuell.
Du musst nicht wissen, welches Tier es ist (
if (tier == hund) ...). Du vertraust darauf, dass es "sprechen" kann.
Merksatz: Die Fähigkeit einer Programmiersprache, Objekte unterschiedlicher Klassen über eine einheitliche Schnittstelle anzusprechen, wobei sich das konkrete Verhalten erst zur Laufzeit entscheidet.
- Overriding (Überschreiben): Die Tochter-Klasse ändert das Verhalten der Mutter.
class Mutter { essen() { "Brot" } }class Kind extends Mutter { essen() { "Brei" } } - Overloading (Überladen): Gleicher Name, andere Parameter. (Gibt es in JS/Python nicht nativ, nur in Java/C#/C++).
print(String s)vs.print(int i).
1. V-Table (Virtual Method Table)
Wie weiß C++ zur Laufzeit, welche Funktion es aufrufen soll?
Jedes Objekt hat einen versteckten Pointer auf eine Tabelle (V-Table).
Darin stehen die Adressen der Funktionen seiner Klasse.
Beim Aufruf tier->machGeräusch() schaut C++ in die Tabelle: "Ah, bei diesem Objekt liegt die Funktion an Adresse 0xA123".
Das kostet einen winzigen Performance-Aufschlag (Pointer Indirection), ermöglicht aber Polymorphie.
2. Duck Typing (Python/JS)
"Wenn es aussieht wie eine Ente und quakt wie eine Ente, ist es eine Ente."
In dynamischen Sprachen gibt es keine Interfaces.
Du kannst machGeräusch() auf jedem Objekt aufrufen.
Wenn das Objekt die Methode hat -> Gut.
Wenn nicht -> Runtime Error.
Maximale Flexibilität, aber weniger Sicherheit als in Java (Compile Time Check).
3. Ad-hoc Polymorphism
Das Überladen von Operatoren.
+ funktioniert für Zahlen (1+1=2) und für Strings ("a"+"b"="ab").
Der Operator verhält sich polymorph.
Dynamic Dispatch und die V-Table Magie
Wie entscheidet die CPU zur Laufzeit (Runtime), welche der vererbten draw()-Funktionen ausgeführt wird?
In kompilierenden OOP-Sprachen (C++, Java) passiert dies über eine Virtual Method Table (vtable).
Jede Klasse, die erbende Polymorphie unterstützt, enthält intern ein Array an Funktionszeigern (Pointern). Wenn du ein Kind-Objekt als Elternteil tarnst und form.draw() aufrufst, indiziert das Programm zuerst den vptr (Virtual-Pointer) des konkreten Speicher-Objekts, springt in dessen vtable und findet die Pointer-Adresse zur Kreis::draw()-Speicherstelle (Dynamic Dispatch). Das ist genial, bedeutet aber: Jeder Methodenaufruf kostet eine Extra-Pointer-Auflösung (Performance-Hit im Nanosekunden-Bereich im Vergleich zu hardverlinktem C-Funktionscode).
Ad-hoc Polymorphismus vs. Parametrischer Polymorphismus
Man differenziert scharf die Art des Gestaltwechsels:
- Ad-Hoc (Überladung): Du deklarierst
print(String)undprint(Int). Für die CPU sind das faktisch zwei völlig getrennte Funktionen, die nur für den Programmierer zufällig gleich heißen. Die Sprache löst schon beim Kompilieren auf, welchen Namen sie meint (Static Binding). - Subtype (Vererbung): Die klassische OOP-Variante, bei der Basis-Klassen überschrieben werden (Late Binding via V-Table).
- Parametrischer Polymorphismus (Generics): Du schreibst
class Liste<T>. Die Logik für "Item einfügen" bleibt für Strings und Booleans strukturell zu 100% identisch. Es ist eine abstrakte Form der Typ-Freiheit (in C++ extrem mächtig durch "Templates" gelöst).
Das Liskovsche Substitutionsprinzip (SOLID)
Polymorphie kann die Architektur zerstören, wenn sie logisch falsch angewandt wird. Liskov fordert: Subtypen müssen sich ohne Überraschungen wie ihr Supertyp verhalten.
Klassik-Desaster Quadrat erbt von Rechteck.
Ein Rechteck hat setWidth() und setHeight(). Funktioniert polymorph einwandfrei. Ein Quadrat erbt das. Wenn jemand blind polymorph rechteck.setWidth(10) und setHeight(5) auf ein ihm unbekanntes Quadrat-Objekt ausführt, mutiert das Quadrat zu einem physikalischen Fehlerfall. Das überschriebene Verhalten des Quadrats bricht die Vertrags-Erwartung des Rechtecks (Breite != Höhe). Dieses Design vernichtet die Integrität von Polymorphie komplett.
Quick-Check
Interface vs Abstract Class?
Interface = Nur Vertrag ("Du musstessen()können"). Abstrakte Klasse = Vertrag + Basis-Code ("Ich zeig dir, wie man kaut, aber was du schluckst, ist deine Sache").Late Binding?
Das technische Wort für Polymorphie. Die Verknüpfung "Methodenname -> Maschinencode" passiert erst spät (zur Laufzeit), nicht früh (beim Kompilieren).Liskov Substitution Principle?
WennEntevonVogelerbt, muss ich überall, woVogelerwartet wird, eineEntereinstecken können, ohne dass das Programm abstürzt. Polymorphie muss sicher sein.