Polymorphie

Aus Byte-Welt Wiki
Zur Navigation springenZur Suche springen

Polymorphie (griechisch, „Vielgestaltigkeit“) ist ein Konzept der Programmierung, das es erlaubt, einem Wert oder einem Namen (z. B. einer Variablen) mehrere Datentypen zuzuordnen.

Den Gegensatz dazu bildet Monomorphie. Dort ist jeder Name und jeder Wert von genau einem Typ.

Einleitung

Polymorphie ist ein Konzept in Programmiersprachen. Um dieses Konzept zu verstehen, ist ein grundlegendes Verständnis von Programmierung und Datentypen notwendig. In dieser Einführung wird die Motivation dahinter verdeutlicht und gleich die verschiedenen Arten der Polymorphie vorweggenommen. Sie werden in den entsprechenden Abschnitten genauer erklärt.

In älteren typisierten Programmiersprachen wird jedem Namen und jedem Wert im Quelltext eines Programms höchstens ein Typ zugeordnet. Die folgenden Beispiele stammen aus keiner konkreten Sprache, sind so aber denkbar:

  • Der Name einer Variablen hat genau den Typ, der ihr bei der Deklaration zugewiesen wurde.
  • Der Wert einer Zeichenkette ist von einem Typ, der üblicherweise mit "String" bezeichnet wird.
  • Das Literal 42 (oder der Wert, den es bezeichnet) hat einen Ganzzahltyp, oft mit "Integer" oder einer Kurzform davon bezeichnet.
  • Der Operator + hat den Typ <math>Integer \times Integer \rightarrow Integer</math>, akzeptiert also genau zwei Argumente vom Typ Integer und liefert genau einen Wert vom Typ Integer
  • Die Funktion second, die zwei Strings akzeptiert und immer den zweiten zurückliefert ist vom Typ <math>String \times String \rightarrow String</math>

Bei diesen Beispielen fallen ein paar interessante Dinge ins Auge. Typischerweise kann der Operator + nicht nur dazu verwendet werden, ganze Zahlen zu addieren, sondern auch reelle oder auch eine ganze Zahl mit einer reellen. In manchen Sprachen wird er auch zur Verknüpfung von Strings verwendet: Man kann den Operator + "überladen", d.h. für verschiedene Parametertypen definieren oder ganze Zahlen implizit, also versteckt in reelle umwandeln (Coercion). Der Operator + besitzt also (vielleicht implizit) verschiedene Typen. Bemerkung: der Typ eines Operators oder einer Funktion besteht aus den Typen der Parameter, deren Reihenfolge und dem Rückgabetyp.

Die Funktion second könnte auch für andere Parametertypen geschrieben werden. Es spielt keine Rolle, von welchem Typ die Parameter sind (Achtung: der Rückgabetyp muss immer genau der des zweiten Parameters sein). Dazu werden die Parametertypen variabel gelassen, genauso wie die Argumente durch die Parameter variabel gelassen werden (parametrische Polymorphie).

Viele arithmetische Operatoren sind auf Ganzzahlen, reellen oder auch komplexen Zahlen definiert. Den verschiedenen Zahlenarten ist also etwas gemeinsam, nämlich, dass man mit ihnen multiplizieren, addieren usw. kann. Einigen wohnt auch eine Ordnung inne, d.h. man kann bestimmen, welche von zwei Zahlen größer oder kleiner als die andere ist. Dabei variieren die Implementierungen der Operationen oft. Dazu führt man einen Typ mit dem Namen Number ein und sagt, dass alle Zahltypen nicht nur von ihrem speziellen Typ (z.B. Integer), sondern gleichzeitig auch vom Typ Number sein können (Inklusionspolymorphie).

Kurze Ergänzung: Nach Sprachbrockhaus ist Polymorphismus (Vielgestaltigkeit) die Ausbildung verschiedenartiger Formen in einer Organismenform

Arten der Polymorphie

In objektorientierten Programmiersprachen gibt es folgende Polymorphismen:

  1. Überladene Funktionen sind polymorphe Funktionen.
  2. Ein Objekt einer Klasse verweist auf eine polymorphe Struktur, wenn es nicht nur auf Objekte einer Klasse sondern auf Objekte verschiedener Klassen, die von einer gemeinsamen Basisklasse abgeleitet wurden, zeigt.
  3. Eine Methode ist polymorph, wenn sie in verschiedenen Klassen die gleiche Spezifikation hat aber unterschiedlich implementiert ist.


Polymorphie wird unterteilt in folgende Arten:

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

Manchmal wird Ad-hoc-Polymorphie gleichgesetzt mit Überladen. Das ist auf Christopher Strachey zurückzuführenQuelle?, der als erster Polymorphie unterteilte und zwar in parametrische und Ad-hoc-Polymorphie. Seine formlose Beschreibung lässt vermuten, dass er bei letzterem tatsächlich nur Überladen im Sinn hatte.

Luca Cardelli und Peter Wegner schlugen die feinere Unterteilung wie oben aufgeführt vor und betrachten Coercion als eine Form von PolymorphieQuelle?.

Universelle und Ad-hoc-Polymorphie

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

Variablen sind überladen, wenn unterschiedliche Funktionen mit dem selben Namen verbunden sind. Beispielsweise ist der Operator + in vielen Programmiersprachen von vornherein überladen. So können mit ihm ganze Zahlen und Fließkommzahlen 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.

Zum Additionsbeispiel von oben gesellen sich noch zwei weitere hinzu:

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 Fließkommazahlen 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.

Parametrische Polymorphie

Parametrisierte Polymorphie repräsentiert Typen, deren Definitionen Typvariablen enthalten. Dabei können Funktionen in der Regel einen oder mehrere Typparameter haben und die zulässigen Typen besitzen in der Regel eine gemeinsame Struktur. Die polymorphen Funktionen haben implizite oder explizite Typparameter, die den Typ des Funktionsergebnisses für jede Anwendung der Funktion festlegen.

Beispiel:

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

Inklusionspolymorphie

Man unterscheidet zwischen Kompilationszeit-Polymorphie und Laufzeit-Polymorphie.

Kompilationszeit-Polymorphie bedeutet, dass zur Kompilationszeit der Typ des Objekts die aufgerufene Funktion (auch "Methode" genannt) bestimmt werden kann.

Laufzeit-Polymorphie bedeutet, dass erst zur Laufzeit bestimmt wird, welche Methode aufzurufen ist (späte Bindung). Es kann also vom Programmlauf abhängig sein, welche Methode in Anwendung kommt. Die Laufzeit-Polymorphie ist einer der wichtigsten Bestandteile der objektorientierten Programmierung und wurde zuerst in der Programmiersprache Smalltalk umgesetzt. Ein weiteres Beispiel für späte Bindung sind generische Methoden wie in CLOS.

Beispiel

Angenommen, eine Anwendung soll statistische Daten sowohl grafisch als auch textuell 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 */ }
 }
Dieser Beitrag stammt in seiner ersten oder einer späteren Version der deutschsprachigen Wikipedia. Er ist dort unter Polymorphie_(Programmierung) zu finden, die Liste der bisherigen Autoren befindet sich in der Versionsliste.