Ковариантность:
class Super {
Object getSomething(){}
}
class Sub extends Super {
String getSomething() {}
}
Sub # getSomething ковариантна, потому что она возвращает подкласс типа возвращаемого Super # getSomething (но выполняет контракт Super.getSomething ())
Контравариантность
class Super{
void doSomething(String parameter)
}
class Sub extends Super{
void doSomething(Object parameter)
}
Sub # doSomething контравариантна потому что он принимает параметр суперкласса параметра Super # doSomething (но, опять же, выполняет контракт Super # doSomething)
Примечание: этот пример не работает в Java. Компилятор Java перегрузит и не переопределит метод doSomething (). Другие языки поддерживают этот стиль контравариантности.
Generics
Это также возможно для Generics:
List<String> aList...
List<? extends Object> covariantList = aList;
List<? super String> contravariantList = aList;
Теперь вы можете получить доступ ко всем методам covariantList
, которые не принимают общий параметр (поскольку он должен быть что-то «расширяет Object»), но геттеры будут работать нормально (поскольку возвращаемый объект всегда будет иметь тип «Object»)
Обратное верно для contravariantList
: вы можете получить доступ ко всем методам с общими параметрами (вы знаете, что это должен быть суперкласс String, поэтому вы всегда можете передать его), но без геттеров (возвращаемый тип может быть любого другого супертипа String)
Посмотрите на принцип замещения Лискова. По сути, если класс B расширяет класс A, то вы должны иметь возможность использовать B везде, где требуется A.
Ковариация: Итерируемая и Итераторная. Почти всегда имеет смысл определить ковариант Iterable
или Iterator
. Iterator расширяет T>
может использоваться так же, как Iterator
- единственное место, где появляется параметр type, - это возвращаемый тип из метода next
, поэтому его можно безопасно привести к T
. Но если у вас есть S
расширяет T
, вы также можете назначить Iterator
переменной типа Iterator расширяет T>
. Например, если вы определяете метод find:
boolean find(Iterable<Object> where, Object what)
, вы не сможете вызвать его с помощью List
и 5
, поэтому его лучше определить как
boolean find(Iterable<?> where, Object what)
Contra-variance: Comparator. Почти всегда имеет смысл использовать Компаратор супер Т>
, потому что его можно использовать так же, как Компаратор
. Параметр type отображается только как тип параметра метода compare
, так что T
можно смело передавать ему. Например, если у вас есть DateComparator реализует Comparator
и вы хотите отсортировать List
с этим компаратором (java.sql.Date
является подклассом java.util.Date
), вы можете сделать с:
<T> void sort(List<T> what, Comparator<? super T> how)
,но не с
<T> void sort(List<T> what, Comparator<T> how)