Polymorphie (Programmierung)

aus Wikipedia, der freien Enzyklopädie
Wechseln zu: Navigation, Suche

Polymorphie oder Polymorphismus (griechisch für Vielgestaltigkeit) ist ein Konzept in der objektorientierten Programmierung, das ermöglicht, dass ein Bezeichner abhängig von seiner Verwendung unterschiedliche Datentypen annimmt. In älteren typisierten Programmiersprachen wird dagegen jedem Namen und jedem Wert im Quelltext eines Programms höchstens ein Typ zugeordnet.

Das Gegenteil der Polymorphie ist die Monomorphie. Hier ist die Variable oder das Literal während der gesamten Laufzeit von genau einem Datentyp.

Arten der Polymorphie[Bearbeiten]

Polymorphie überladener Operatoren[Bearbeiten]

Ein Bezeichner, der für einen Operator steht (bspw. „+“, „−“ oder „minus“), kann mehrmals mit anderer Bedeutung implementiert werden. Für jeden Kontext, in dem der Operator neu deklariert wurde, muss die Implementierung immer eindeutig sein.

Polymorphie der Objektorientierten Programmierung[Bearbeiten]

Die Polymorphie der objektorientierten Programmierung ist eine Eigenschaft, die immer im Zusammenhang mit Vererbung und Schnittstellen (Interfaces) auftritt. Eine Methode ist polymorph, wenn sie in verschiedenen Klassen die gleiche Signatur hat, jedoch erneut implementiert ist.

Gibt es in einem Vererbungszweig einer Klassenhierarchie mehrere Methoden auf unterschiedlicher Hierarchieebene, jedoch mit gleicher Signatur, wird erst zur Laufzeit bestimmt, welche der Methoden für ein gegebenes Objekt verwendet wird (Dynamisches Binden). Bei einer mehrstufigen Vererbung wird jene Methode verwendet, die direkt in der Objektklasse (d. h. jene Klasse, von der das Objekt ein Exemplar ist) definiert ist, oder jene, die im Vererbungszweig am weitesten „unten“ liegt (d. h. die Methode, die von der Vererbung her am nächsten ist).

Moderne Konzepte kennen jedoch auch Polymorphie über Klassengrenzen hinaus. So erlaubt Objective-C die Polymorphie zwischen zwei gleichnamigen Methoden, die in verschiedenen Klassen erstmals definiert sind.

// NSObject kennt nicht -doSomething
@interface KlasseA : NSObject {}
- (void) doSomething;
@end
 
@interface KlasseB : NSObject {}
- (void) doSomething;
@end
// irgendwo
id object =// Ein Objekt einer beliebigen Klasse
[object doSomething]; // polymorph zwischen KlasseA und KlasseB

Selbstverständlich gilt die Subklasse-vor-Basisklasse-Regel auch hier: Wenn die KlasseB zur KlasseC abgeleitet wird, würde die entsprechende Methode der KlasseC ausgeführt.

Polymorphie einer Funktion bzw. Prozedur[Bearbeiten]

Ist der Rückgabewert oder ein Argument einer Funktion polymorph, heißt die Funktion polymorphe Funktion. Mit Hilfe polymorpher Funktionen kann die Generizität von Datenstrukturen auch in Algorithmen angewandt werden.[1]

Polymorphie von Datentypen oder Klassen[Bearbeiten]

Wird für eigene Datentypen bzw. Klassen bei der Instanziierung bzw. beim Konstruktoraufruf ein Parameter für den tatsächlich verwendeten Datentyp übergeben, spricht man von parametrischer Polymorphie, welche semantisch mit Generizität übereinstimmt.

Weitere Unterteilung[Bearbeiten]

Folgende weitere Unterteilung ist möglich:

  • universelle Polymorphie
    • parametrische Polymorphie
    • Inklusions-/Vererbungspolymorphie
  • Ad-hoc-Polymorphie
    • Coercion
    • Überladung

Manchmal wird Ad-hoc-Polymorphie gleichgesetzt mit Überladen. Das ist auf Christopher Strachey zurückzuführen,[2] der als Erster Polymorphie unterteilte und zwar in parametrische und Ad-hoc-Polymorphie.

Luca Cardelli und Peter Wegner erweiterten Stracheys Konzept um die Inklusionspolymorphie[3], um Subtypen und Vererbung zu modellieren. Die obige Auflistung spiegelt also Stracheys Einteilung wider, erweitert um die Inklusionspolymorphie von Cardelli und Wegner.

Universelle und Ad-hoc-Polymorphie[Bearbeiten]

Universelle Polymorphie unterscheidet sich von Ad-hoc-Polymorphie in mehreren Aspekten. Bei Ad-hoc-Polymorphie kann ein Name oder ein Wert nur endlich viele verschiedene Typen besitzen. Diese sind zudem während der Kompilierung bekannt. Universelle Polymorphie dagegen erlaubt es, unendlich viele Typen zuzuordnen.

Ein weiterer Unterschied liegt darin, dass die Implementierung einer universell polymorphen Funktion generell gleichen Code unabhängig von den Typen ihrer Argumente ausführt, während ad-hoc-polymorphe (also überladene) Funktionen abhängig von den Typen ihrer Argumente unterschiedlich implementiert sein können.

Überladen und Coercion[Bearbeiten]

Variablen sind überladen, wenn unterschiedliche Funktionen mit demselben Namen verbunden sind. Beispielsweise ist der Operator + in vielen Programmiersprachen von vornherein überladen. So können mit ihm ganze Zahlen und Gleitkommazahlen addiert werden. Oft wird er auch zur Stringkonkatenierung verwendet:

 42 + 3              (1)
 3.14 + 1.0          (2)
 "Hallo" + " Welt!"  (3)

Einige Sprachen unterscheiden dabei, welche Namen überladen werden dürfen und welche nicht. In Java ist Methodenüberladung erlaubt, Operatorüberladung außer der schon eingebauten Überladung, wie des +-Operators aber nicht. C++ und andere Sprachen erlauben generell beides.

Coercion ist eine Art implizite Typumwandlung, um zum Beispiel Argumente einer Funktion in die von der Funktion erwarteten Typen umzuwandeln. Coercion ist mit dem Überladen eng verknüpft und Unterschiede sind für den Programmierer nicht unbedingt gleich ersichtlich.

Beispiel:

 3.14 + 2            (4)
 3 + 2.14            (5)

In einer Sprache könnte der Additionsoperator lediglich für zwei reelle Zahlen definiert sein. Coercion würde dann dafür sorgen, dass ganze Zahlen zuerst in Gleitkommazahlen umgewandelt werden. In (4) und (5) würde dann Coercion zum Einsatz kommen. Es ist aber auch denkbar, dass der Additionsoperator für mehrere Varianten definiert ist.

Bei der Überladung handelt es sich offenbar nicht um eine echte Form von Polymorphie, da man sich vorstellen könnte, der Compiler werde die Uneindeutigkeit durch die mehrfache Benutzung eines Symboles zur Kompilationszeit wieder auflösen. Wir erlauben also nur einem Symbol, verschiedene Werte zu denotieren, die allerdings unterschiedliche und möglicherweise zueinander inkompatible Typen haben.

Mit Coercions verhält es sich ähnlich. Man könnte meinen, ein Operator akzeptiere Operanden verschiedenen Typs (wie das + oben), jedoch müssen die Typen erst für den Operator gewandelt werden. Der Ausgabetyp des Operators hängt also nicht mehr mit den Typen der Operanden zusammen (oder nur partiell), daher kann keine echte Polymorphie vorliegen.

Parametrische Polymorphie[Bearbeiten]

Parametrisierte Polymorphie repräsentiert Typen, deren Definitionen Typvariablen enthalten. In Java spricht man auch von generischen Typen oder Generics. Die meisten modernen objektorientierten Programmiersprachen unterstützen parametrische Typdefinitionen, darunter auch Strongtalk (eine Variante von Smalltalk mit Typsystem), C# oder Eiffel. In C++ können generische Typen mit Hilfe sogenannter Templates nachgebildet werden.

Beispiel:

  • monomorph
TYPE iContainer IS ARRAY OF INTEGER;
  • polymorph durch Typvariable
TYPE Stack IS ARRAY OF [TYPVARIABLE]

Beschränkter parametrischer Polymorphismus[Bearbeiten]

Man unterscheidet grundsätzlich

  • einfachen parametrischen Polymorphismus und
  • beschränkten parametrischen Polymorphismus.

Letzterer behebt die Probleme der Typsicherheit, die innerhalb von Typdefinitionen dadurch entstehen, dass beim Erstellen der Typdefinition auf Grund der Parametrisierung noch nicht klar ist, Objekte welchen Typs eigentlich Gegenstand der Typ-Operationen (des Protokolls, der Methoden, die Terminologie variiert hier je nach Programmiersprache) sind. Wird durch einen Typ beispielsweise eine numerische Operation definiert, die auf den Elementen des Typs ausführbar sein soll, seine Typvariable dann aber mit einem nichtnumerischen Typen belegt, so würde es zu Laufzeitfehlern kommen. In der Regel verwendet man daher beschränkte parametrische Typen, die für ihre Typvariablen eine Beschränkung auf bestimmte Typen angeben. In Strongtalk wird hierzu bspw. die Typvariable mittels T < Supertyp angegeben, wobei Supertyp die Einschränkung der Typen angibt, die in die Typvariable T eingesetzt werden können. Java ermöglicht die Angabe solcher Einschränkungen mittels der Schreibweise <T extends Supertyp>.

Inklusionspolymorphie[Bearbeiten]

Inklusionspolymorphie bezeichnet die Eigenschaft, jede Methode statt auf einem Subtyp auch auf einem Basistypen ausführen zu können. Subtyping ist demnach eine Form der Inklusionspolymorphie.

Man unterscheidet zwischen

  • Kompilationszeit-Polymorphie (statisches Binden)
    Es kann zur Kompilationszeit der Typ des Objekts die aufgerufene Funktion (auch „Methode“ genannt) bestimmt werden.
  • Laufzeit-Polymorphie (dynamisches Binden).
    Erst zur Laufzeit kann bestimmt werden, welche Methode aufzurufen ist (späte Bindung). Es kann also vom Programmlauf abhängig sein, welche Methode zur Anwendung kommt. Die Laufzeit-Polymorphie ist einer der wichtigsten Bestandteile der objektorientierten Programmierung und wurde zuerst in der Programmiersprache Smalltalk umgesetzt und zum Beispiel in Objective-C eingesetzt. Ein weiteres Beispiel für späte Bindung sind generische Methoden wie im Common Lisp Object System.

Beispiel[Bearbeiten]

Angenommen, eine Anwendung soll statistische Daten sowohl grafisch als auch schriftlich in Tabellenform darstellen. Außerdem soll es möglich sein, die Darstellungsmethoden über Plugins zu erweitern. Dann erlaubt das Konzept der Polymorphie über das VisualizationPlugin Interface jede beliebige Implementierung (hier GraphDisplayPlugin, TextDisplayPlugin, HistogramDisplayPlugin) aufzurufen.

Die Anwendung selbst muss bei neuen Plugins nicht geändert werden und kann diese einfach über das Interface mit dem Aufruf von setData und display starten.

 interface VisualizationPlugin {
   public void setData( DisplayData data );
   public void display();
 }
 class GraphDisplayPlugin implements VisualizationPlugin {
   public void setData( DisplayData data ) { /* set data to be displayed */ }
   public void display() { /* Show Data as Graph */ }
 }
 class TextDisplayPlugin implements VisualizationPlugin {
   public void setData( DisplayData data ) { /* set data to be displayed */ }
   public void display() { /* Show Data as table */ }
 }
 class HistogramDisplayPlugin implements VisualizationPlugin {
   public void setData( DisplayData data ) { /* set data and calculate history data */ }
   public void display() { /* Show history data as Graph */ }
 }

Literatur[Bearbeiten]

Weblinks[Bearbeiten]

Einzelnachweise[Bearbeiten]

  1. Informatik. Duden, Mannheim 2001, ISBN 978-3-411-10023-1, S. 496
  2. Fundamental concepts in programming languages. Lecture notes for International Summer School in Computer Programming, Copenhagen, Aug. 1967
  3. On Understanding Types, Data Abstraction, and Polymorphism. ACM Computing Surveys, 17(4):471–522, 1985