Antworten:
Kovarianz:
class Super {
Object getSomething(){}
}
class Sub extends Super {
String getSomething() {}
}
Sub # getSomething ist kovariant, da es eine Unterklasse des Rückgabetyps von Super # getSomething zurückgibt (aber den Vertrag von Super.getSomething () erfüllt).
Kontravarianz
class Super{
void doSomething(String parameter)
}
class Sub extends Super{
void doSomething(Object parameter)
}
Sub # doSomething ist kontravariant, da es einen Parameter einer Oberklasse des Parameters von Super # doSomething verwendet (aber auch hier den Vertrag von Super # doSomething erfüllt).
Hinweis: Dieses Beispiel funktioniert nicht in Java. Der Java-Compiler würde die doSomething () - Methode überladen und nicht überschreiben. Andere Sprachen unterstützen diesen Stil der Kontravarianz.
Generika
Dies ist auch für Generika möglich:
List<String> aList...
List<? extends Object> covariantList = aList;
List<? super String> contravariantList = aList;
Sie können jetzt auf alle Methoden zugreifen, für covariantList
die kein generischer Parameter erforderlich ist (da es sich um etwas handeln muss, das "Objekt erweitert"), aber Getter funktionieren einwandfrei (da das zurückgegebene Objekt immer vom Typ "Objekt" ist).
Das Gegenteil gilt für contravariantList
: Sie können auf alle Methoden mit generischen Parametern zugreifen (Sie wissen, dass es sich um eine Oberklasse von "String" handeln muss, damit Sie immer eine übergeben können), aber keine Getter (Der zurückgegebene Typ kann von einem anderen Supertyp von String sein )
Ko-Varianz: Iterierbar und Iterator. Es ist fast immer sinnvoll, eine Co-Variante Iterable
oder zu definieren Iterator
. Iterator<? extends T>
kann genauso verwendet werden wie Iterator<T>
- der einzige Ort, an dem der Typparameter angezeigt wird, ist der Rückgabetyp der next
Methode, sodass er sicher auf den neuesten Stand gebracht werden kann T
. Wenn Sie jedoch S
Erweiterungen haben T
, können Sie auch Iterator<S>
eine Variable vom Typ zuweisen Iterator<? extends T>
. Zum Beispiel, wenn Sie eine Suchmethode definieren:
boolean find(Iterable<Object> where, Object what)
Sie können es nicht mit List<Integer>
und aufrufen 5
, daher ist es besser definiert als
boolean find(Iterable<?> where, Object what)
Gegenvarianz: Komparator. Es ist fast immer sinnvoll zu verwenden Comparator<? super T>
, da es genauso verwendet werden kann Comparator<T>
. Der Typparameter wird nur als compare
Methodenparametertyp angezeigt und T
kann daher sicher an ihn übergeben werden. Wenn Sie beispielsweise eine haben DateComparator implements Comparator<java.util.Date> { ... }
und eine List<java.sql.Date>
mit diesem Komparator sortieren möchten ( java.sql.Date
eine Unterklasse von java.util.Date
), können Sie Folgendes tun:
<T> void sort(List<T> what, Comparator<? super T> how)
aber nicht mit
<T> void sort(List<T> what, Comparator<T> how)
Schauen Sie sich das Liskov-Substitutionsprinzip an . Wenn Klasse B Klasse A erweitert, sollten Sie in der Lage sein, ein B zu verwenden, wenn ein A erforderlich ist.
contra variant
. super.doSomething("String")
konnte nicht ersetzt werden durch sub.doSomething(Object)
.