Podaj przykłady funkcji, które demonstrują kowariancję i kontrawariancję zarówno w przypadku przeciążania, jak i nadpisywania w Javie? [Zamknięte]

Odpowiedzi:

155

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, covariantListktó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 )

Mocno zakodowane
źródło
79
Pierwszy przykład kontrawariancji nie działa w Javie. doSomething () w klasie Sub jest przeciążeniem, a nie przesłonięciem.
Craig P. Motlin,
15
W rzeczy samej. Java nie obsługuje kontrawariantnych argumentów w podtypach. Tylko kowariancja dla typów zwracanych metod (jak w pierwszym przykładzie).
the_dark_destructor
Świetna odpowiedź. Kowariancja wydaje mi się logiczna. Ale czy mógłbyś wskazać mi akapit w JLS, który opisuje sprzeczność? Dlaczego wywoływany jest Sub.doSomething?
Michaił
2
Jak zauważył Craig, nie jest. Myślę, że tutaj dochodzi do starcia między zastępowaniem a przeciążaniem, a SUN wybrał (jak zawsze) opcję kompatybilną wstecz. Tak więc w Javie nie można używać parametrów kontrawariantnych podczas zastępowania metody.
Hardcoded
1
Byłoby miło wiedzieć, dlaczego otrzymuję negatywne głosy za moją odpowiedź.
Zakodowany
48

Kowariancja: Iterowalna i Iteratorowa. Niemal zawsze sensowne jest zdefiniowanie równoległego wariantu Iterablelub 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 nextmetody, więc można go bezpiecznie przesłać do góry T. Ale jeśli masz Srozszerzenia 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 comparetyp parametru metody, więc Tmoż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.Datejest 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)
Yardena
źródło
-4

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.

extraneon
źródło
6
To nie jest odpowiedzią na pytanie i wprowadza w błąd. Byłoby całkowicie możliwe zaprojektowanie systemu wariantowego, który łamie poprawność semantyczną, a tym samym narusza LSP.
Matt Whipple,
tak nie jest, contra variantpowiedzmy. super.doSomething("String")nie można zastąpić sub.doSomething(Object).
zinking
Nie o to chodzi
OlivierTerrien