Kovarianz (Informatik)

Kovarianz (Informatik)

In der objektorientierten Programmierung bedeutet Kovarianz und Kontravarianz, ob ein Aspekt gleichartig der Vererbungsrichtung (kovariant) oder entgegengesetzt zu dieser (kontravariant) ist. Liegt in der Unterklasse keine Änderung gegenüber der Oberklasse vor, wird das als Invarianz bezeichnet.

Den Begriffen liegen die Überlegungen des Ersetzbarkeitsprinzips zugrunde: Objekte der Oberklasse müssen durch Objekte einer ihrer Unterklassen ersetzbar sein. Das bedeutet zum Beispiel, dass die Methoden der Unterklasse mindestens die Parameter akzeptieren müssen, die die Oberklasse auch akzeptieren würde (Kontravarianz). Die Methoden der Unterklasse müssen ebenfalls Werte zurückliefern, die mit der Oberklasse vereinbar sind, also nie allgemeineren Typs sind, als der Rückgabetyp der Oberklasse (Kovarianz).

Inhaltsverzeichnis

Begriffsherkunft

Die Begriffe Kontravarianz und Kovarianz leiten sich in der Objektorientierung davon ab, dass sich die Typen der betrachteten Parameter mit der Vererbungshierarchie der Ersetzung (kovariant) beziehungsweise entgegengesetzt zur Vererbungshierarchie (kontravariant) verhalten.

Auftreten von Varianzen

Man kann zwischen Ko-, Kontra- und Invarianz bei

  • Methoden
    • Argumenttypen (die Typen der übergebenen Parameter)
    • Ergebnistypen (die Typen des Rückgabewertes)
    • sonstige Signaturerweiterungen (z.B. Exceptiontypen in der throws-Klausel in Java)
  • generischen Klassenparametern

unterscheiden.

Durch das Substitutionsprinzip ergeben sich in der Polymorphie der objektorientierten Programmierung folgende Auftrittsmöglichkeiten für Varianzen:

Kontravarianz Eingabeparameter
Kovarianz Rückgabewert und Ausnahmen
Invarianz Ein- und Ausgabeparameter

Kovarianz, Kontravarianz und Invarianz

Kovarianz bedeutet, dass die Typhierarchie mit der Vererbungshierarchie der zu betrachtenden Klassen die gleiche Richtung hat. Wenn man also eine ererbte Methode anpassen will, so ist die Anpassung kovariant, wenn der Typ eines Methodenparameters in der Oberklasse ein Obertyp des Parametertyps dieser Methode in der Unterklasse ist.

Wenn die Typhierarchie entgegengesetzt zur Vererbungshierarchie der zu betrachtenden Klassen läuft, so spricht man von Kontravarianz. Wenn die Typen in der Ober- und Unterklasse nicht geändert werden dürfen, spricht man von Invarianz.

In der Objektorientierten Modellierung ist es oft wünschenswert, dass auch die Eingabeparameter von Methoden kovariant sind. Dadurch wird allerdings das Substitutionsprinzip verletzt. Das Überladen wird in diesem Fall von den verschiedenen Programmiersprachen unterschiedlich gehandhabt.

1. Beispiel anhand von Abbildungen

Im Folgenden wird verdeutlicht, wann die Typsicherheit gewährleistet bleibt, wenn man eine Funktion durch eine andere ersetzen will. Dies lässt sich im Weiteren dann auf Methoden in der Objektorientierung übertragen, wenn nach dem Liskovschen Substitutionsprinzip Methoden von Objekten ersetzt werden.

Seien f1 und f2 Funktionen, die beispielsweise folgende Signatur haben:

f_1: A \rightarrow B , wobei A = \{12, \ldots, 18\} und B = \{30, \ldots, 65\}, und

f_2: C \rightarrow D , wobei C = \{10, \ldots, 20\} und D = \{40, \ldots, 60\}.

Wie man sieht, ist C eine Obermenge von A, jedoch D eine Untermenge von B. Wenn man die Funktion f2 anstelle von f1 einsetzt, dann nennt man den Eingabetyp C kontravariant, den Ausgabetyp D kovariant. Im Beispiel kann die Ersetzung ohne Typverletzung geschehen, da die Eingabe von f2 den gesamten Bereich der Eingabe von f1 abdeckt. Außerdem liefert f2 Ergebnisse, die den Wertebereich von f1 nicht überschreiten.

2. Beispiel für Kovarianz

Beispiel C++: Beispiel Java:
 class T {
     T* self () { return this; }
     ...
 };

 class S : public T {
     S* self () { return this; }
     ...
 };
 public class ClassA {
     public ClassA self() { return this; }
     ...   
 }

 public class ClassB extends ClassA {
     public ClassB self() { return this; }
     ...
 }

Korrektheit von Kontra- und Kovarianz

Als Modell soll die UML-Schreibweise zur Darstellung der Vererbungshierarchie dienen:

                       Kontravarianz           Kovarianz             Invarianz
 +---------+         +---------------+     +---------------+     +---------------+  
 |    T    |         | ClassA        |     | ClassA        |     | ClassA        |  
 +---------+         +---------------+     +---------------+     +---------------+  
 |         |         |               |     |               |     |               |  
 +---------+         +---------------+     +---------------+     +---------------+  
 |         |         | method(t':T') |     | method():T    |     | method(t :T&) |  
 +---------+         +---------------+     +---------------+     +---------------+  
      ^                      ^                     ^                     ^
      |                      |                     |                     | 
      |                      |                     |                     | 
 +---------+         +---------------+     +---------------+     +---------------+  
 |    T'   |         | ClassB        |     | ClassB        |     | ClassB        |  
 +---------+         +---------------+     +---------------+     +---------------+  
 |         |         |               |     |               |     |               |  
 +---------+         +---------------+     +---------------+     +---------------+  
 |         |         | method(t :T ) |     | method():T'   |     | method(t :T&) |  
 +---------+         +---------------+     +---------------+     +---------------+  

Kontravarianz: Das Substitutionsprinzip wird eingehalten, denn man kann method(t : T) der Unterklasse ClassB so verwenden, als wäre es die Methode der Oberklasse ClassA.
Prüfen: Man kann der method(t : T) eine Variable eines spezielleren Typs T' übergeben, da aufgrund der Vererbung T' alle Informationen enthält, die sich auch in T befinden.

Kovarianz: Das Substitutionsprinzip wird eingehalten, denn man kann method():T' der Unterklasse ClassB so verwenden, als wäre es die Methode der Oberklasse ClassA.
Prüfen: Der Rückgabewert der Methode aus ClassB ist T'. Man darf diesen Wert einer vom Typ T deklarierten Variable übergeben, da T' aufgrund der Vererbung über alle Informationen verfügt, die sich auch in T befinden.

Typsicherheit bei Methoden

Auf Grund der Eigenschaften des Substitutionsprinzipes ist statische Typsicherheit dann gewährleistet, wenn die Argumenttypen kontravariant und die Ergebnistypen kovariant sind.

Typunsichere Kovarianz

Die in der Objektorientierten Modellierung oft wünschenswerte Kovarianz der Methodenparameter wird trotz resultierender Typunsicherheit in vielen Programmiersprachen unterstützt.

Ein Beispiel für die Typunsicherheit kovarianter Methodenparameter findet sich in den folgenden Klassen Person und Arzt, und deren Spezialisierungen Kind und Kinderarzt. Der Parameter der Methode untersuche in der Klasse Kinderarzt ist eine Spezialisierung des Parameters derselben Methode von Arzt und demnach kovariant.

Typunsichere Kovarianz - allgemein
  +---------+         +---------------+
  |    T    |         | ClassA        |
  +---------+         +---------------+
  |         |         |               |
  +---------+         +---------------+
  |         |         | method(t :T ) |
  +---------+         +---------------+
       ^                      ^        
       |                      |        
       |                      |        
  +---------+         +---------------+
  |    T'   |         | ClassB        |
  +---------+         +---------------+
  |         |         |               |
  +---------+         +---------------+
  |         |         | method(t':T') |
  +---------+         +---------------+
   Beispiel für typunsichere Kovarianz
  +----------------+         +-----------------------+
  | Person         |         | Arzt                  |
  +----------------+         +-----------------------+
  |                |         |                       |
  +----------------+         +-----------------------+
  | stillHalten()  |         | untersuche(p: Person) |
  +----------------+         +-----------------------+
           ^                             ^        
           |                             |        
           |                             |        
  +----------------+         +-----------------------+
  | Kind           |         | Kinderarzt            |
  +----------------+         +-----------------------+
  |                |         |                       |
  +----------------+         +-----------------------+
  | tapferSein()   |         | untersuche(k: Kind)   |
  +----------------+         +-----------------------+

In Java-Code:

Die Implementierung des Beispiels in Java sieht folgendermaßen aus:
public class Person {
    public void stillHalten() {...}
}
public class Kind extends Person {
    public void tapferSein() {...}
}
public class Arzt {
    public void untersuche(Person person) {
        person.stillHalten();
    }
}
public class Kinderarzt extends Arzt {
    public void untersuche(Kind kind) {
        kind.stillHalten();
        kind.tapferSein();
    }
}
   Ein Programm unter Verwendung der Klassen könnte so aussehen:
public class Main {
    public static void main(String[] args) {
        Arzt arzt = new Kinderarzt();
        Person person = new Person();
        arzt.untersuche(person);
    } 
}

Ein Kinderarzt darf wegen der Subtypbeziehung als Arzt auftreten. Die Methode untersuche von Arzt akzeptiert einen Parameter vom Typ Person. In der überschriebenen Methode untersuche in Kinderarzt wird aber die Methode tapferSein an dem Parameter aufgerufen - eine Methode, die ein Objekt vom allgemeinen Typ Person nicht zur Verfügung stellt. Die Forderung nach Spezialisierung des Parameters auf ein Objekt vom Typ Kind wird nicht berücksichtigt. Der Aufruf würde einen Fehler verursachen.

In Java funktioniert das Beispiel dennoch: die Methode untersuche von Arzt wird in Kinderarzt nicht überschrieben sondern aufgrund der unterschiedlichen Parameter lediglich überladen. Laut der Sprachdefinition von Java muss eine Methode welche überschrieben werden soll, die gleiche Signatur (in Java bestehend aus Parameter + evtl. Exceptions) besitzen.

Siehe auch


Wikimedia Foundation.

Schlagen Sie auch in anderen Wörterbüchern nach:

  • Invarianz (Informatik) — In der objektorientierten Programmierung bedeutet Kovarianz und Kontravarianz, ob ein Aspekt gleichartig der Vererbungsrichtung (kovariant) oder entgegengesetzt zu dieser (kontravariant) ist. Liegt in der Unterklasse keine Änderung gegenüber der… …   Deutsch Wikipedia

  • Kontravarianz (Informatik) — In der objektorientierten Programmierung bedeutet Kovarianz und Kontravarianz, ob ein Aspekt gleichartig der Vererbungsrichtung (kovariant) oder entgegengesetzt zu dieser (kontravariant) ist. Liegt in der Unterklasse keine Änderung gegenüber der… …   Deutsch Wikipedia

  • Covarianz — Der Begriff Kovarianz bezeichnet eine Maßzahl in der Statistik, siehe Kovarianz (Stochastik) ein Konzept der objektorientierten Programmierung, siehe Kovarianz (Informatik) ein Transformationsverhalten mathematischer oder physikalischer Größen,… …   Deutsch Wikipedia

  • Kovariant — Der Begriff Kovarianz bezeichnet eine Maßzahl in der Statistik, siehe Kovarianz (Stochastik) ein Konzept der objektorientierten Programmierung, siehe Kovarianz (Informatik) ein Transformationsverhalten mathematischer oder physikalischer Größen,… …   Deutsch Wikipedia

  • Überladen (Programmierung) — Eine Programmiersprache ermöglicht das Überladen eines Bezeichners, wenn mehrere Vereinbarungen mit demselben Bezeichner gleichzeitig sichtbar sein können; bei der Verwendung erfolgt dann die Auswahl anhand des Kontextes. Mit anderen Worten… …   Deutsch Wikipedia

  • Generische Programmierung in Java 5.0 — Generische Programmierung wird in Java durch so genannte Generics ermöglicht. Der Begriff steht synonym für „parametrisierte Typen“. Die Idee dahinter ist zusätzliche Variablen für Typen, sog. Typ Variablen, einzuführen. Diese repräsentieren zum… …   Deutsch Wikipedia

  • Java (Technologie) — Java Logo Java Technik (englisch Java Technology) ist eine hauptsächlich von Sun Microsystems entwickelte Sammlung von Spezifikationen, die einerseits die Programmiersprache Java und andererseits verschiedene Laufzeitumgebungen für… …   Deutsch Wikipedia

  • Ersetzbarkeitsprinzip — Das liskovsche Substitutionsprinzip (LSP) oder Ersetzbarkeitsprinzip ist ein Kriterium in der objektorientierten Programmierung, das angibt, wann ein Datentyp als Subtyp eines anderen Typs modelliert werden kann. Das liskovsche… …   Deutsch Wikipedia

  • Liskov-Prinzip — Das liskovsche Substitutionsprinzip (LSP) oder Ersetzbarkeitsprinzip ist ein Kriterium in der objektorientierten Programmierung, das angibt, wann ein Datentyp als Subtyp eines anderen Typs modelliert werden kann. Das liskovsche… …   Deutsch Wikipedia

  • Substituierbarkeitsprinzip — Das liskovsche Substitutionsprinzip (LSP) oder Ersetzbarkeitsprinzip ist ein Kriterium in der objektorientierten Programmierung, das angibt, wann ein Datentyp als Subtyp eines anderen Typs modelliert werden kann. Das liskovsche… …   Deutsch Wikipedia

Share the article and excerpts

Direct link
Do a right-click on the link above
and select “Copy Link”