2010-03-23 16 views

Odpowiedz

119

kowariancji:

class Super { 
    Object getSomething(){} 
} 
class Sub extends Super { 
    String getSomething() {} 
} 

Sub getSomething jest kowariantna ponieważ zwraca podklasę zwracanego typu Super # getSomething (ale spełniają wymagania umowę Super.getSomething())

kontrawariancja

class Super{ 
    void doSomething(String parameter) 
} 
class Sub extends Super{ 
    void doSomething(Object parameter) 
} 

Sub doSomething jest contrava Riant, ponieważ bierze parametr superklasy parametru Super # doSomething (ale znowu wypełnia kontrakt Super # doSomething)

Uwaga: ten przykład nie działa w Javie. Kompilator Java mógłby przeciążyć i nie zastąpić metody doSomething() -. Inne języki wspierają ten styl kontrawariancji.

Generics

jest to również możliwe dla rodzajowych:

List<String> aList... 
List<? extends Object> covariantList = aList; 
List<? super String> contravariantList = aList; 

Możesz teraz uzyskać dostęp do wszystkich metod covariantList że nie bierze parametru rodzajowego (jak to musi być coś "extends Object"), ale moduły pobierające działają poprawnie (ponieważ zwracany obiekt zawsze będzie typu "Object")

Odwrotnie jest w przypadku contravariantList: Możesz uzyskać dostęp do wszystkich metod z parametrami ogólnymi (wiesz, że musi to być klasa nadrzędna "Łańcucha", więc zawsze możesz je przekazać), ale nie ma modułów pobierających (zwracany typ może być dowolnego innego typu nadtypu ciągu znaków)

+68

Pierwszy przykład kontrawariancji nie działa w Javie. doSomething() w klasie Sub jest przeciążeniem, a nie przesłonięciem. –

+13

Rzeczywiście. Java nie obsługuje sprzecznych argumentów w podtypach. Tylko kowariancja dla zwracanych typów metod (jak w pierwszym przykładzie). –

+0

Świetna odpowiedź. Kowariancja wygląda dla mnie logicznie. Ale czy mógłbyś wskazać mi akapit w JLS, który opisuje sprzeczność? Dlaczego wywoływana jest funkcja Sub.doSomething? – Mikhail

1

Spójrz na Liskov substitution principle. W efekcie, jeśli klasa B rozszerza klasę A, powinieneś być w stanie użyć B, gdy wymagane jest A.

+2

This nie odpowiada na pytanie i wprowadza w błąd. Byłoby całkowicie możliwe zaprojektowanie wariantowego systemu, który łamie semantyczną poprawność, a zatem narusza LSP. –

+0

Tak nie jest w przypadku "contra variant". 'super.doSomething (" String ")' nie może być zastąpione przez 'sub.doSomething (Object)'. – zinking

38

Współzmienność: Iterable i Iterator. Prawie zawsze ma sens zdefiniowanie co-wariantu Iterable lub Iterator. Iterator<? extends T> może być używany tylko jako Iterator<T> - jedynym miejscem, w którym pojawia się parametr type, jest typ zwracany z metody , więc można go bezpiecznie przenieść do wersji T. Ale jeśli masz S rozszerza T, możesz również przypisać Iterator<S> do zmiennej typu Iterator<? extends T>. Na przykład, jeśli są zdefiniowaniu metody znajdują się:

boolean find(Iterable<Object> where, Object what) 

nie będzie mógł nazwać go List<Integer> i 5, więc lepiej zdefiniowane jako

boolean find(Iterable<?> where, Object what) 

przeciwwskazane wariancji: porównawcze. To prawie zawsze ma sens, aby użyć Comparator<? super T>, ponieważ może być używany tak jak Comparator<T>. Parametr type pojawia się tylko jako typ parametru metody compare, dzięki czemu T może zostać do niego bezpiecznie przekazany.Na przykład, jeśli masz DateComparator implements Comparator<java.util.Date> { ... } i chcesz posortować List<java.sql.Date> z tego komparatora (java.sql.Date to podklasa java.util.Date), można zrobić z:

<T> void sort(List<T> what, Comparator<? super T> how) 

ale nie z

<T> void sort(List<T> what, Comparator<T> how)