Polymorphie
Polymorphie (griechisch fuer "Vielgestaltigkeit") ist eines der fundamentalen Konzepte der objektorientierten Programmierung (OOP). Sie ermoeglicht es, dass Objekte unterschiedlicher Klassen auf denselben Methodenaufruf unterschiedlich reagieren koennen. Zusammen mit Vererbung und Kapselung bildet Polymorphie die drei Saeulen der OOP.
Definition und Grundprinzip
Das Wort Polymorphie stammt aus dem Griechischen und setzt sich aus "poly" (viel) und "morph" (Gestalt) zusammen. In der Programmierung bedeutet dies: Eine Methode kann in verschiedenen Klassen unterschiedlich implementiert sein, obwohl sie denselben Namen und dieselbe Signatur traegt.
Der entscheidende Vorteil: Du kannst Objekte verschiedener Klassen einheitlich behandeln, solange sie eine gemeinsame Schnittstelle oder Oberklasse teilen. Der konkrete Code, der ausgefuehrt wird, haengt vom tatsaechlichen Typ des Objekts zur Laufzeit ab.
Arten der Polymorphie
In der objektorientierten Programmierung unterscheidet man zwei Hauptarten von Polymorphie, die zu unterschiedlichen Zeitpunkten aufgeloest werden:
Statische Polymorphie (Compile-Zeit)
Bei der statischen Polymorphie wird bereits zur Compile-Zeit entschieden, welche Methode aufgerufen wird. Das bekannteste Beispiel ist die Methodenueberladung (Method Overloading): Mehrere Methoden tragen denselben Namen, unterscheiden sich aber in ihrer Parameterliste.
public class Rechner {
// Ueberladene Methoden mit gleichem Namen
public int addiere(int a, int b) {
return a + b;
}
public double addiere(double a, double b) {
return a + b;
}
public int addiere(int a, int b, int c) {
return a + b + c;
}
}
Der Compiler waehlt anhand der uebergebenen Argumente die passende Methode aus. Dies geschieht vor der Programmausfuehrung, daher "statisch".
Dynamische Polymorphie (Laufzeit)
Die dynamische Polymorphie ist das eigentliche Herzstuck der OOP. Hier wird erst zur Laufzeit entschieden, welche Methode ausgefuehrt wird. Sie basiert auf Methodenueberschreibung (Method Overriding): Eine Unterklasse definiert eine Methode der Oberklasse neu.
// Abstrakte Oberklasse
abstract class Tier {
public abstract void gibLaut();
}
// Konkrete Unterklassen
class Hund extends Tier {
@Override
public void gibLaut() {
System.out.println("Wuff!");
}
}
class Katze extends Tier {
@Override
public void gibLaut() {
System.out.println("Miau!");
}
}
// Polymorphe Verwendung
public class Main {
public static void main(String[] args) {
Tier meinTier = new Hund(); // Referenz vom Typ Tier
meinTier.gibLaut(); // Ausgabe: Wuff!
meinTier = new Katze(); // Gleiche Referenz, anderes Objekt
meinTier.gibLaut(); // Ausgabe: Miau!
}
}
Das Schluesselprinzip: Die Variable meinTier hat den Typ Tier, aber das tatsaechliche Verhalten wird durch den konkreten Objekttyp (Hund oder Katze) bestimmt.
Polymorphie mit Interfaces
Neben Vererbung kannst du Polymorphie auch durch Interfaces (Schnittstellen) erreichen. Verschiedene Klassen implementieren dasselbe Interface und koennen dadurch einheitlich behandelt werden:
// Interface definiert den Vertrag
interface Zeichenbar {
void zeichne();
}
// Verschiedene Klassen implementieren das Interface
class Kreis implements Zeichenbar {
@Override
public void zeichne() {
System.out.println("Zeichne einen Kreis");
}
}
class Rechteck implements Zeichenbar {
@Override
public void zeichne() {
System.out.println("Zeichne ein Rechteck");
}
}
// Polymorphe Verarbeitung
public void alleZeichnen(List<Zeichenbar> formen) {
for (Zeichenbar form : formen) {
form.zeichne(); // Jede Form weiss selbst, wie sie sich zeichnet
}
}
Interfaces sind besonders wertvoll, weil sie Polymorphie ohne Vererbungshierarchie ermoeglichen. Eine Klasse kann mehrere Interfaces implementieren und dadurch in verschiedenen polymorphen Kontexten verwendet werden.
Vorteile der Polymorphie
Polymorphie bietet erhebliche Vorteile fuer die Softwareentwicklung und ist ein Grund, warum objektorientierte Programmierung so verbreitet ist:
- Erweiterbarkeit: Neue Klassen koennen hinzugefuegt werden, ohne bestehenden Code zu aendern
- Wartbarkeit: Aenderungen an einer Klasse betreffen nicht den Code, der sie polymorph verwendet
- Wiederverwendbarkeit: Generischer Code funktioniert mit allen kompatiblen Typen
- Lesbarkeit: Einheitliche Schnittstellen machen den Code verstaendlicher
- Testbarkeit: Mock-Objekte koennen echte Implementierungen ersetzen
Polymorphie in verschiedenen Programmiersprachen
Das Konzept der Polymorphie existiert in allen objektorientierten Sprachen, wird aber unterschiedlich umgesetzt:
Java
Java verwendet das Schluesselwort extends fuer Vererbung und implements fuer Interfaces. Methoden sind standardmaessig ueberschreibbar, ausser sie sind als final markiert. Die Annotation @Override dokumentiert beabsichtigte Ueberschreibungen.
C#
In C# muessen Methoden explizit als virtual deklariert werden, um ueberschreibbar zu sein. Die Ueberschreibung erfolgt mit dem Schluesselwort override. Dies macht Polymorphie sichtbarer im Code.
class Tier {
public virtual void GibLaut() {
Console.WriteLine("Tier macht Geraeusch");
}
}
class Hund : Tier {
public override void GibLaut() {
Console.WriteLine("Wuff!");
}
}
Python
Python nutzt Duck Typing: "Wenn es wie eine Ente geht und wie eine Ente quakt, ist es eine Ente." Objekte muessen nicht von derselben Klasse erben - es genuegt, wenn sie die erwarteten Methoden besitzen.
class Hund:
def gib_laut(self):
return "Wuff!"
class Katze:
def gib_laut(self):
return "Miau!"
# Polymorphie durch Duck Typing
def tier_geraeusch(tier):
print(tier.gib_laut())
tier_geraeusch(Hund()) # Wuff!
tier_geraeusch(Katze()) # Miau!
Praxisbeispiel: Plugin-System
Ein klassisches Anwendungsbeispiel fuer Polymorphie ist ein Plugin-System. Der Hauptcode definiert eine Schnittstelle, und verschiedene Plugins implementieren diese auf ihre eigene Weise:
// Plugin-Interface
interface ExportPlugin {
String getFormat();
void exportiere(Dokument dok);
}
// Konkrete Plugins
class PDFExport implements ExportPlugin {
@Override
public String getFormat() { return "PDF"; }
@Override
public void exportiere(Dokument dok) {
// PDF-spezifische Logik
}
}
class CSVExport implements ExportPlugin {
@Override
public String getFormat() { return "CSV"; }
@Override
public void exportiere(Dokument dok) {
// CSV-spezifische Logik
}
}
// Verwendung - der Code kennt nur das Interface
class Anwendung {
private List<ExportPlugin> plugins = new ArrayList<>();
public void registriere(ExportPlugin plugin) {
plugins.add(plugin);
}
public void exportiereAlle(Dokument dok) {
for (ExportPlugin plugin : plugins) {
plugin.exportiere(dok); // Polymorpher Aufruf
}
}
}
Neue Export-Formate koennen hinzugefuegt werden, ohne den Code der Anwendung zu aendern. Das ist die Staerke von Polymorphie: Offen fuer Erweiterung, geschlossen fuer Aenderung (Open/Closed-Prinzip).
Polymorphie in der IT-Praxis
Polymorphie ist kein abstraktes Konzept, sondern begegnet dir in nahezu jedem objektorientierten Softwareprojekt. Wer als Fachinformatiker fuer Anwendungsentwicklung arbeitet, wird Polymorphie taeglich einsetzen - sei es bei der Arbeit mit Frameworks, beim Implementieren von Design Patterns oder beim Testen mit Mock-Objekten.