Zum Inhalt springen
L

Wikipedia · einfach zusammengefasst · Stand

Polymorphie (Programmierung)

Polymorphie oder Polymorphismus (griechisch für Vielgestaltigkeit) ist ein Konzept in der objektorientierten Programmierung, das ermöglicht, …

Inhalt5 Abschnitte
  1. 1. Grundidee und Bedeutung
  2. 2. Polymorphie bei Vererbung und Interfaces
  3. 3. Universelle, parametrische und beschränkte Polymorphie
  4. 4. Inklusionspolymorphie und Bindungszeit
  5. 5. Überladung und Coercion

Grundidee und Bedeutung

Polymorphie bedeutet in der Programmierung, dass ein Bezeichner abhängig von seiner Verwendung Objekte unterschiedlicher Datentypen annehmen kann. Dadurch lässt sich derselbe Name oder dieselbe Schnittstelle für verschiedene Typen und Implementierungen verwenden. Das ist besonders für wiederverwendbare und erweiterbare Programme wichtig.

Das Gegenstück ist die Monomorphie: In älteren typisierten Programmiersprachen wird jedem Namen und jedem Wert im Quelltext höchstens ein Typ zugeordnet.

Polymorphie kann sich auf unterschiedliche Bestandteile eines Programms beziehen:

  • Ein Operator wie „+“ oder „−“ kann für verschiedene Datentypen unterschiedlich implementiert sein. In jedem Verwendungskontext muss eindeutig feststehen, welche Implementierung gemeint ist.
  • Eine Funktion oder Prozedur heißt polymorph, wenn ihr Rückgabewert oder eines ihrer Argumente polymorph ist. So können generische Datenstrukturen auch in Algorithmen verwendet werden.
  • Datentypen oder Klassen sind parametrisch polymorph, wenn bei ihrer Instanziierung oder beim Konstruktoraufruf ein Parameter festlegt, welcher Datentyp tatsächlich verwendet wird. Dies entspricht semantisch der Generizität.

Polymorphie bei Vererbung und Interfaces

In der objektorientierten Programmierung tritt Polymorphie im Zusammenhang mit Vererbung und Schnittstellen, sogenannten Interfaces, auf. Eine Methode ist polymorph, wenn sie in verschiedenen Klassen dieselbe Signatur besitzt, aber jeweils neu implementiert wird. Die Signatur beschreibt dabei die für den Aufruf entscheidenden Merkmale einer Methode.

Existieren in einem Zweig einer Klassenhierarchie mehrere Methoden mit gleicher Signatur, wird beim dynamischen Binden erst zur Laufzeit entschieden, welche Implementierung für ein bestimmtes Objekt ausgeführt wird. Bei mehrstufiger Vererbung gilt:

  • Verwendet wird die Methode, die direkt in der tatsächlichen Objektklasse definiert ist.
  • Ist dort keine entsprechende Methode definiert, wird im Vererbungszweig die am weitesten unten liegende und damit nächstgelegene Methode verwendet.

Diese Subklasse-vor-Basisklasse-Regel sorgt dafür, dass eine spezialisierte Klasse das Verhalten ihrer Basisklasse überschreiben kann. Ein Programm kann ein Objekt über einen allgemeineren Typ oder ein Interface ansprechen, während zur Laufzeit trotzdem die passende spezialisierte Implementierung ausgeführt wird.

Objective-C erlaubt außerdem Polymorphie zwischen gleichnamigen Methoden, die erstmals in verschiedenen, nicht notwendigerweise durch Vererbung verbundenen Klassen definiert wurden. Eine Variable des Typs id kann ein Objekt einer beliebigen Klasse enthalten. Beim Aufruf von doSomething kann deshalb je nach tatsächlichem Objekt die Implementierung aus KlasseA oder KlasseB ausgeführt werden. Wird KlasseB zu KlasseC abgeleitet und implementiert KlasseC die Methode selbst, wird wiederum die Methode der Subklasse KlasseC gewählt.

Universelle, parametrische und beschränkte Polymorphie

Eine mögliche Einteilung unterscheidet universelle Polymorphie, parametrische Polymorphie, Inklusions- beziehungsweise Vererbungspolymorphie, Ad-hoc-Polymorphie, Coercion und Überladung.

Bei Ad-hoc-Polymorphie kann der Wert, der einem Namen zugeordnet ist, nur endlich vielen verschiedenen Typen angehören. Diese Typen sind bereits während der Kompilierung bekannt. Die Implementierung darf abhängig von den Argumenttypen völlig unterschiedlich sein. Ad-hoc-Polymorphie wird daher manchmal mit Überladung gleichgesetzt.

Universelle Polymorphie erlaubt dagegen die Zuordnung jedes bekannten Typs, einschließlich Typen, die möglicherweise erst später definiert werden. Eine universell-polymorphe Funktion führt grundsätzlich unabhängig von den Typen ihrer Argumente denselben Code aus.

Parametrische Polymorphie verwendet Typdefinitionen, die Typvariablen enthalten. Eine Typvariable ist ein Platzhalter für den später eingesetzten konkreten Datentyp. In Java heißen solche Typen generische Typen oder Generics. Parametrische Typdefinitionen werden unter anderem von Strongtalk, C# und Eiffel unterstützt; in C++ können sie mithilfe von Templates nachgebildet werden. Ein monomorpher Container kann beispielsweise ausdrücklich als Array von INTEGER definiert sein. Ein polymorpher Stack wird dagegen als Array einer Typvariable definiert und kann dadurch mit unterschiedlichen Elementtypen verwendet werden.

Beim parametrischen Polymorphismus unterscheidet man die einfache und die beschränkte Form. Bei einer uneingeschränkten Typvariable ist während der Definition noch unbekannt, welche Objekte später verwendet werden. Verlangt eine Operation beispielsweise numerische Elemente, wird die Typvariable aber mit einem nichtnumerischen Typ belegt, können Laufzeitfehler entstehen.

Beschränkter parametrischer Polymorphismus verbessert deshalb die Typsicherheit, indem er die zulässigen konkreten Typen einschränkt. Strongtalk verwendet dafür beispielsweise T < Supertyp, Java die Schreibweise <T extends Supertyp>. C# kennt mehrere Arten solcher Einschränkungen, die dort Constraints heißen. Ein Typparameter kann etwa von einer bestimmten Basisklasse abgeleitet sein müssen, ein bestimmtes Interface oder einen parameterlosen Konstruktor besitzen. Mehrere Constraints können kombiniert werden; dann müssen alle erfüllt sein.

Inklusionspolymorphie und Bindungszeit

Inklusionspolymorphie bezeichnet die Eigenschaft, eine Methode statt nur auf einem Subtyp auch über einen Basistyp ausführen zu können. Ein Subtyp ist ein speziellerer Typ, der dort eingesetzt werden kann, wo der allgemeinere Basistyp erwartet wird. Subtyping ist damit eine Form der Inklusionspolymorphie.

Nach dem Zeitpunkt der Methodenauswahl werden zwei Formen unterschieden:

  • Bei der Kompilationszeit-Polymorphie oder dem statischen Binden kann der Typ des Objekts und damit die aufzurufende Methode bereits während der Übersetzung bestimmt werden.
  • Bei der Laufzeit-Polymorphie oder dem dynamischen beziehungsweise späten Binden steht erst während des Programmlaufs fest, welche Methode aufzurufen ist. Die Auswahl kann somit vom konkreten Ablauf und vom tatsächlichen Objekt abhängen.

Laufzeit-Polymorphie ist ein wichtiger Bestandteil der objektorientierten Programmierung. Sie wurde zuerst in Smalltalk umgesetzt und beispielsweise in Objective-C eingesetzt. Auch generische Methoden im Common Lisp Object System werden als Beispiel für späte Bindung genannt.

Ein typischer Anwendungsfall ist eine Anwendung, die statistische Daten grafisch oder als Tabelle darstellt und durch Plug-ins erweiterbar sein soll. Das Interface VisualizationPlugin deklariert die Methoden setData und display. Klassen wie GraphDisplayPlugin, TextDisplayPlugin und HistogramDisplayPlugin implementieren dieses gemeinsame Interface auf unterschiedliche Weise. Die Anwendung ruft nur die im Interface festgelegten Methoden auf. Deshalb muss sie beim Hinzufügen eines neuen Darstellungs-Plug-ins nicht verändert werden; zur Laufzeit wird die Implementierung des jeweiligen Plug-ins verwendet.

Ein weiteres Beispiel verwendet in C# das Interface Polygon mit der Methode berechneFlaecheninhalt(). Die Klassen Rechteck und Dreieck implementieren sie jeweils passend. Die Funktion gibFlaecheninhalt(Polygon polygon) kann beide Objekte entgegennehmen und über denselben Methodenaufruf die richtige Berechnung ausführen. Für ein Rechteck mit Breite 12 und Höhe 16 ergibt sich der Flächeninhalt 192. Für ein Dreieck mit den Seitenlängen 4, 13 und 15 ergibt die verwendete Heron-Formel den Flächeninhalt 24.

Überladung und Coercion

Bei der Überladung ist unterschiedliches Verhalten mit demselben Namen verbunden. Das gilt für Funktionen ebenso wie für Operatoren. Der Operator + ist in vielen Programmiersprachen bereits überladen: Er kann ganze Zahlen wie in 42 + 3, Gleitkommazahlen wie in 3.14 + 1.0 oder Zeichenketten wie in "Hallo" + " Welt!" verknüpfen.

Programmiersprachen erlauben Überladung in unterschiedlichem Umfang. Java gestattet Methodenüberladung, aber keine selbst definierte Operatorüberladung; bereits eingebaute Fälle wie die Überladung von + bleiben möglich. Auch in C# sind nicht alle Operatoren überladbar. C++ und manche andere Sprachen erlauben grundsätzlich sowohl Methoden- als auch Operatorüberladung.

Coercion ist eine implizite Typumwandlung. Man kann sie sich als unsichtbare, automatisch eingefügte Funktion vorstellen, die beispielsweise Argumente in die von einer Funktion erwarteten Typen umwandelt. Coercion ist eng mit Überladung verbunden, sodass der Unterschied im Programmtext nicht immer sofort sichtbar ist.

Bei 3.14 + 2 und 3 + 2.14 könnte eine Sprache den Additionsoperator ausschließlich für zwei reelle Zahlen definieren. Dann würden die ganzen Zahlen durch Coercion zunächst in Gleitkommazahlen umgewandelt. Alternativ könnte der Operator für mehrere Kombinationen von Typen überladen sein.

Der Artikel schränkt ein, dass Überladung offenbar keine echte Polymorphie darstellt: Der Compiler kann die Mehrdeutigkeit eines mehrfach verwendeten Symbols schon zur Kompilationszeit auflösen. Das Symbol bezeichnet dann mehrere funktionsartige Werte mit unterschiedlichen und möglicherweise inkompatiblen Typen. Eine ähnliche Einschränkung gilt für Coercion. Obwohl ein Operator scheinbar verschiedene Operandentypen akzeptiert, werden diese zunächst in die tatsächlich erwarteten Typen umgewandelt. Der Ausgabetyp hängt dadurch nicht oder nur teilweise mit den ursprünglichen Operandentypen zusammen, weshalb auch hier keine echte Polymorphie vorliegen kann.

Lernvideos zu Polymorphie (Programmierung)

Weiterlesen

Objektorientierte Programmierung Die objektorientierte Programmierung (kurz OOP) ist ein auf dem Konzept der Objektorientierung basierendes Programmierparadigma. Die Grundidee besteht darin … Objekt (Programmierung) Ein Objekt in der Programmierung bezeichnet eine inhaltlich zusammengehörige Datenmenge: In der objektorientierten Programmierung wird eine Objektinstanz … Datentyp Die Konkretisierung der Operationsmenge führt zu Abstrakten Datentypen beziehungsweise Algebraischen Strukturen. Mit der weiteren Konkretisierung der … Typisierung (Informatik) B. Variablen, Funktionen oder Objekte (im Sinne der objektorientierten Programmierung) korrekt verwendet werden. Ein Ziel der Typisierung ist die Vermeidung von … Programmiersprache Bei deklarativen Programmiersprachen ist der Ausführungsalgorithmus schon vorab festgelegt und wird nicht im Quelltext ausformuliert/beschrieben, sondern es … Quelltext Quelltext, auch Quellcode (englisch source code) oder unscharf Programmcode genannt, ist in der Informatik der für Menschen lesbare, in einer … Programmierung Beim Programmieren sind wesentliche Aspekte zur Softwarequalität zu berücksichtigen und durch die Gestaltung des Quellcodes umzusetzen. Siehe dazu als Beispiele … Vererbung (Programmierung) Die Vererbung dient dazu, aufbauend auf existierenden Klassen neue zu schaffen, wobei die Beziehung zwischen ursprünglicher und neuer Klasse dauerhaft ist. Eine … Schnittstelle (Objektorientierung) Eine Schnittstelle oder ein Interface (englisch interface oder protocol) definiert in der objektorientierten Programmierung, welche Methoden in den … Methode (Programmierung) Methoden (englisch method oder member function) sind in der objektorientierten Programmierung Unterprogramme in der Form von Funktionen oder Prozeduren, … Klasse (Objektorientierung) Die Klasse dient als Bauplan für die Abbildung von realen Objekten in Softwareobjekte und beschreibt Attribute (Eigenschaften) und Methoden (Verhaltensweisen) … Signatur (Programmierung) Signaturen spielen eine Rolle bei der Polymorphie, einem der grundlegenden Konzepte der Objektorientierung. In vielen Programmiersprachen kann eine Methode …