Odpowiedzi:
Kowariancja:
class Super {
Object getSomething(){}
}
class Sub extends Super {
String getSomething() {}
}
Sub # getSomething jest kowariantna, ponieważ zwraca podklasę zwracanego typu Super # getSomething (ale wypełnia kontrakt Super.getSomething ())
Sprzeczność
class Super{
void doSomething(String parameter)
}
class Sub extends Super{
void doSomething(Object parameter)
}
Sub # doSomething jest kontrawariantne, ponieważ przyjmuje parametr klasy nadrzędnej parametru Super # doSomething (ale ponownie wypełnia kontrakt z Super # doSomething)
Uwaga: ten przykład nie działa w Javie. Kompilator Java przeładowałby i nie przesłaniałby metody doSomething () - Method. Inne języki wspierają ten styl kontrawariancji.
Generics
Jest to również możliwe w przypadku Generics:
List<String> aList...
List<? extends Object> covariantList = aList;
List<? super String> contravariantList = aList;
Możesz teraz uzyskać dostęp do wszystkich metod, covariantList
które nie przyjmują ogólnego parametru (ponieważ musi to być coś, co „rozszerza obiekt”), ale metody pobierania będą działać dobrze (ponieważ zwracany obiekt będzie zawsze typu „Object”)
Odwrotnie jest w przypadku contravariantList
: Możesz uzyskać dostęp do wszystkich metod z parametrami ogólnymi (wiesz, że musi to być nadklasa "String", więc zawsze możesz ją przekazać), ale bez funkcji pobierających (Zwrócony typ może być dowolnego innego nadtypu String )
Kowariancja: Iterowalna i Iteratorowa. Niemal zawsze sensowne jest zdefiniowanie równoległego wariantu Iterable
lub Iterator
. Iterator<? extends T>
może być używany tak samo, jak Iterator<T>
- jedynym miejscem, w którym pojawia się parametr typu, jest typ zwracany z next
metody, więc można go bezpiecznie przesłać do góry T
. Ale jeśli masz S
rozszerzenia T
, możesz również przypisać je Iterator<S>
do zmiennej typu Iterator<? extends T>
. Na przykład, jeśli definiujesz metodę wyszukiwania:
boolean find(Iterable<Object> where, Object what)
nie będziesz w stanie go wywołać za pomocą List<Integer>
i 5
, więc lepiej zdefiniować go jako
boolean find(Iterable<?> where, Object what)
Kontra-wariancja: komparator. Prawie zawsze ma sens Comparator<? super T>
, ponieważ można go używać tak samo, jak Comparator<T>
. Parametr typu pojawia się tylko jako compare
typ parametru metody, więc T
można go bezpiecznie przekazać. Na przykład, jeśli masz DateComparator implements Comparator<java.util.Date> { ... }
i chcesz posortować a za List<java.sql.Date>
pomocą tego komparatora ( java.sql.Date
jest to podklasa java.util.Date
), możesz zrobić z:
<T> void sort(List<T> what, Comparator<? super T> how)
ale nie z
<T> void sort(List<T> what, Comparator<T> how)
Spójrz na zasadę substytucji Liskova . W efekcie, jeśli klasa B rozszerza klasę A, wtedy powinieneś być w stanie użyć B zawsze, gdy wymagane jest A.
contra variant
powiedzmy. super.doSomething("String")
nie można zastąpić sub.doSomething(Object)
.