Polymorphismus in objektorientierten Systemen

5 Kommentare

Vor kurzem hatte ich eine interessante Diskussion im Büro. Wir schauten uns gerade Go an (eine relativ junge Programmiersprache von Google), als das Gespräch auf den Begriff „Polymorphismus“ kam. Erstaunlicherweise gab es mindestens drei unterschiedliche Auffassungen davon, was dieser Begriff bedeutet. Da mein Verständnis des Begriffs „Polymorphismus“ etwas von dem der anderen Beteiligten abwich, möchte ich es hier zur Diskussion stellen. Ich bin gespannt zu hören, welche Meinungen es noch zu dem Thema gibt.

Polymorphismus und Vererbung

Eine häufige verwendete Definition von Polymorphismus ist eng an den Begriff der Vererbung in objektorientierten Programmiersprachen gekoppelt (die folgende Definition ist von der englischen Wikipedia):

„Subtyping (or inclusion polymorphism) is a concept wherein a name may denote instances of many different classes as long as they are related by some common superclass. In object-oriented programming, this is often referred to simply as polymorphism.“

Dies möchte ich an einem Beispiel erläutern:

public abstract class Person {
   abstract String getName();
}
 
public class Student extends Person {
   @Override
   public String getName() {
      return "Peter";
   }
}
 
public class Professor extends Person {
   @Override
   public String getName() {
      return "Professor Smith";
   }
}
 
public class App {
 
   public static void printName(Person person) {
      System.out.println(person.getName());
   }
 
   public static void main(String... args) {
      Student student = new Student();
      Professor professor = new Professor();
 
      printName(student);
      printName(professor);
   }
}

In diesem Beispiel kann der Name person in der printPerson(Person) Methode Instanzen von verschiedenen Klassen referenzieren (nämlich von Student und Professor). Das Ganze funktioniert, weil beide von Person ableiten, welche die getName() Methode definiert. Der Compiler stellt für uns sicher, dass alle Subklassen eine getName() Methode haben.

Was passiert nun, wenn wir das Beispiel um folgenden Code erweitern:

public class Dog {
   public String getName() {
      return "Pluto";
   }
}

Die Dog Klasse erbt nicht von Person – ein Hund ist nun mal keine Person. Aber alle Hunde haben für gewöhnlich einen Namen, also macht es Sinn eine getName() Methode für Hunde zu definieren. Da Java eine statisch typisierte Sprache ist, können wir Instanzen von Dog nicht an die Methode printName(Person) übergeben, obwohl Instanzen von Dog die Schnittstelle anbieten, welche printPerson(Person) benötigt (nämlich eine Methode mit dem Namen „getName“, die keine Eingangsparameter übernimmt und einen String zurück gibt). Polymorphismus ist in Java also an Vererbung gebunden.

Ein weiteres Problem der obigen Definition des Begriffs „Polymorphismus“ ist, dass diese das Konzept von Klassen voraussetzt. Daraus leitet sich die Frage ab: Gibt es Polymorphismus in JavaScript? Wir haben Polymorphismus auf der Basis von Vererbung zwischen Klassen definiert. Da JavaScript die Bildung von Klassen nicht explizit unterstützt, sieht es so aus, als gäbe es auch kein Polymorphismus in JavaScript.

Polymorphismus ohne Vererbung

An dieser Stelle müssen wir unsere Sicht auf den Begriff „Polymorphismus“ erweitern. Hier ist das obige Beispiel als JavaScript Code:

var student = {
   getName: function() {
      return "Peter";
   }
};
 
var professor = {
   getName: function() {
      return "Professor Smith";
   }
};
 
var printName = function(param) {
   console.log(param.getName());
};
 
printName(student);
printName(professor);

Obowhl es weder Vererbung noch Klassen in JavaScript gibt, sieht es so aus als würde sich die Funktion printName(param) polymorph verhalten (ja, es gibt prototypische Vererbung in JavaScript, aber wir wollen es nicht komplizierter machen, als notwendig ;-)). Was passiert jetzt also, wenn wir ein Hunde Objekt hinzufügen?

var dog = {
   getName: function() {
      return "Pluto";
   }
};

Können wir den Hunde an die printName(param) Funktion übergeben? Natürlich können wir das, da JavaScript dynamisch typisiert ist. Es würde einfach „Pluto“ ausgegeben werden.

Eine allgemeinere Definition von Polymorphismus

Das zweite Beispiel führt zu einer allgemeineren Definition des Begriffs „Polymorphismus“, die nicht an die Konzepte von Klassen und Vererbung gebunden ist, sondern nur an das Kernkonzept der Objektorientierung: Objekte, welche Nachrichten miteinander austauschen.1

„Moment mal… Objekte die Nachrichten austauschen?“ werden Sie jetzt vielleicht sagen.
„Aber natürlich!“ antworte ich.

Der folgende Aufruf in Java:

pers.getName();

kann übersetzt werden in das Senden der Nachricht „bitte gib mir deinen Namen“ an das Objekt, welches von „pers“ referenziert wird. Das Objekt entscheidet dann, ob es die Nachricht beantworten kann. In Java stellt der Compiler sicher, dass wir nur Nachrichten verschicken, die die Empfängerobjekte beantworten können – Code der Methoden aufruft, welche nicht definiert sind, lässt der Compiler nicht zu. In dynamisch typisierten Sprachen können wir jede beliebige Nachricht an unsere Objekte senden, ohne zu wissen ob diese die Nachrichten tatsächlich behandeln können. In unserem Beispiel beantwortet das Objekt die Nachricht, indem es das Ergebnis des Methodenaufrufs von getName() zurück liefert.

Mein Begriffsverständnis von „Polymoprhismus“ formuliere ich also folgendermaßen:

„Polymorphismus in einem objektorientierten System ist die Fähigkeit unterschiedlicher Objekte auf die selbe Nachricht mit verschiedenen Antworten zu reagieren.“

Nach dieser Definition sendet der Code in der Java Methode printName(Person) die Nachricht „gib mir deinen Namen“ an den Eingangsparameter. Instanzen von Klassen, die von Person ableiten, können auf diese Nachricht mit unterschiedlichen Antworten reagieren. Der Compiler stellt dabei sicher, dass alle Objekte, welche an printName(Person) übergeben werden, die Nachricht auch wirklich behandeln können.

Die JavaScript Funktion printName(param) auf der anderen Seite sendet ebenfalls die Nachricht „gib mir deinen Namen“ an ihren Eingangsparameter. Anders als in Java gibt es in JavaScript keine statische Typisierung und keinen Compiler, welcher Parametertypen prüft. Dementsprechend wird auch keine gemeinsame Superklasse benötigt. So lange die Objekte, welche der Funktion übergeben werden die Nachricht „gib mir deinen Namen“ behandeln können, funktioniert alles wunderbar. Wird jedoch ein Objekt übergeben, welches die Nachricht nicht behandeln kann, kommt es zu einem Laufzeitfehler.

Die vorgestellte Definition des Begriffs „Polymorphismus“ ist nicht an die Konzepte von Klassen oder Vererbung gebunden. Sie kann auf statisch typisierte Sprachen mit Vererbung wie Java ebenso angewendet werden, wie auf dynamisch typisierte Sprachen wie JavaScript. Mit anderen Worten: Polymorphismus hängt nicht von Vererbung ab! Im Gegenteil, die Tatsache, dass man Polymorphismus in statisch typisierten Sprachen nur zusammen mit Vererbung nutzen kann, ist sogar eine Einschränkung.

Fußnoten

1. Haben Sie sich schonmal gefragt, welche eigentlich die Kernkonzepte der Objektorientierung sind? Fragen Sie ihre Kollegen danach. Ich bin mir sicher, dass Sie sehr verschiedene Antworten erhalten werden. Die Wahrheit ist, dass es keine formale Definition des Begriffs gibt. Ein sehr gute Lektüre zu diesem Thema ist Armstrong DJ (2006) The quarks of object-oriented development in Communications of the ACM 49(2):123–128 – ich kann sie jedem ans Herz legen, der mit objektorientierten Systemen arbeitet.

Benedikt Ritter arbeitet seit September 2013 als Software Craftsman bei der codecentric AG. Sein Können bringt er nicht nur in der Berufswelt zum Einsatz: Benedikt ist Member der Apache Software Foundation und Committer beim Apache Commons Projekt.

Share on FacebookGoogle+Share on LinkedInTweet about this on TwitterShare on RedditDigg thisShare on StumbleUpon

Kommentare

  • Roman Grom

    20. Februar 2014 von Roman Grom

    Polymorphismus hängt nicht von Vererbung ab! Im Gegenteil, die Tatsache, dass man Polymorphismus in statisch typisierten Sprachen nur zusammen mit Vererbung nutzen kann, ist sogar eine Einschränkung.

    Und wie steht es mit dem Überladen von Methoden? Dazu braucht es keine Vererbung und ich sehe dies als einen Teil der Polymorphie in Java an.

    • Benedikt Ritter

      21. Februar 2014 von Benedikt Ritter

      Hallo Roman,

      danke für den Kommentar. Es hängt von deiner Definition von Polymorphismus ab. Diese Definition betrachtet ja unterschiedliche Objekte, welche dieselbe Nachricht unterschiedlich beantworten können. Bei Überladung beantwortet ein Objekt unterschiedliche Nachrichten mit unterschiedlichen Antworten. Überladen von Methoden würde also nach dieser Definition hier nicht als polymorphes Verhalten zählen.

      Benedikt

      • Roman Grom

        4. März 2014 von Roman Grom

        Benedikt, Du hast Recht. Eine Methode ist polymorph, wenn sie in der Ableitung identische Signatur hat, und neu implementiert wurde. Es ist ihre innere Gestalt, nicht ihre äußere.

  • Simon

    3. Juli 2016 von Simon

    Sehr schön erklärt!!! Vielen Dank. Von den hundert Internetseiten wo ich mich durchgekämpft habe, ist das mit Abstand die beste Erklärung.

Kommentieren

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.