OO: Varianz

„Definition“ Kovarianz

Ein Objekt Foo<T> ist kovariant, falls Foo<Derived> Unterklasse von Foo<Base> ist, also genau dann, wenn überall da wo Foo<Base> steht auch Foo<Drived> stehen kann.

Positives Beispiel

Foo<T> sei der Rückgabwert einer  method(): T. Da der Klient nur Methoden von T aufrufen kann, kann man auch abgeleitete Klassen zurückgeben: da wo Foo<Base> steht kann auch Foo<Drived> stehen. Rückgabewerte von Methoden sind kovariant.

Bis JDK 1.4 war es nicht möglich, dass es bei einer Basisklasse mit Methode

method() : Base

eine Subklasse geben kann mit

method(): Sub.

Erst durch die Einführung von Kovarianz bei Rückgabewerten mit JAVA 5 war das erlaubt. Vormals musste bei der Nutzung gecastet werden

Sub sub = (Sub) sub.method();

Technisch findet ein Überladen statt, da für ein Überschreiben die Methodensignaturen gleich sein müssen. Daher existieren in Methoden-Fall beide Methoden method():Base und method():Sub.

Negatives Beispiel

Stack<T> ist nicht kovariant: String (sub) ist eine Unterklasse von Objekt (base). Betrachte folgenden Code

(1) Stack<String> strings = createStack(String.class);
(2) Stack<Object> objects = strings; // assume assignment is allowed
(3) objects.push(new Object());
(4) String element = strings.pop();

Wäre Stack<T> kovariant, so könnte man dort wo Stack<base> steht auch Stack<String> stehen.  Damit wäre Zeile (2) erlaubt. Das in (3)  erzeugte Objekt wäre aber in (4) auch ein String, was falsch ist.

„Definition“ Kontravarianz

Foo<T> ist kontravariant, falls Foo<Base> eine Spezialisierung von Foo<Derived> ist, d.h. genau dann, wenn überall wo Foo<Derived> steht auch Foo<Base> stehen darf.

Positives Beispiel

Foo<T> sei der Eingangsparameter einer methode(in : T). Sei method(in : sub). Da sub als Unterklasse auch ein base ist, kann in einer spezialisierten Methode auch methode(in : base) stehen. Eingangsparameter sind kontravariant.

Negatives Beispiel

Stack<T> ist auch nicht kontravariant: String (sub) ist eine Unterklasse von Objekt (base). Betrachte folgenden Code

(1) Stack<Object> objects = createStack(Object.class);
(2) Stack<String> strings = objects; // assume assignment is allowed
(3) objects.push(new Object());
(4) String element = strings.pop();

Wäre Stack<T> kontravariant, so könnte man dort wo Stack<String> steht auch Stack<Object> stehen.  Damit wäre wiederum Zeile (2) erlaubt. Das in (3)  erzeugte Objekt wäre aber in (4) auch ein String, was ebenfalls falsch ist.

Arrays sind in JAVA kovariant

Ist Base Baisklasse von Sub, so ist Base[] Basisklasse von Sub[]. Nach dem LSP (Ersetzungsprinzip) kann also überall da, wo Base[] gefordert ist auch Sub[] eingesetzt werden.

Object[] objarray = new String[]{…};

Problematisch hier ist der Laufzeittyp (String). Der Compiler-Typ ist Object. Daher geht man auch davon aus, dass im objarray Integer gespeichert werden könnten. Ist aber zur Laufzeit wegen dem Laufzeittyp nicht möglich.

 

 

 

Schreibe einen Kommentar