Proszę pokazać dobry przykład kowariancji i kontrawariancji w Javie.
źródło
Proszę pokazać dobry przykład kowariancji i kontrawariancji w Javie.
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
lubIterator
.Iterator<? extends T>
może być używany tak samo, jakIterator<T>
- jedynym miejscem, w którym pojawia się parametr typu, jest typ zwracany znext
metody, więc można go bezpiecznie przesłać do góryT
. Ale jeśli maszS
rozszerzeniaT
, możesz również przypisać jeIterator<S>
do zmiennej typuIterator<? extends T>
. Na przykład, jeśli definiujesz metodę wyszukiwania:nie będziesz w stanie go wywołać za pomocą
List<Integer>
i5
, więc lepiej zdefiniować go jakoKontra-wariancja: komparator. Prawie zawsze ma sens
Comparator<? super T>
, ponieważ można go używać tak samo, jakComparator<T>
. Parametr typu pojawia się tylko jakocompare
typ parametru metody, więcT
można go bezpiecznie przekazać. Na przykład, jeśli maszDateComparator implements Comparator<java.util.Date> { ... }
i chcesz posortować a zaList<java.sql.Date>
pomocą tego komparatora (java.sql.Date
jest to podklasajava.util.Date
), możesz zrobić z:ale nie z
źródło
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.
źródło
contra variant
powiedzmy.super.doSomething("String")
nie można zastąpićsub.doSomething(Object)
.