Пожалуйста, покажите хороший пример для ковариации и контравариантности в Java.
Продемонстрировать ковариацию и контравариантность в Java?
Ответ 1
ковариации:
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() - Method. Другие языки поддерживают этот стиль контравариантности.
Дженерики
Это также возможно для Generics:
List<String> aList...
List<? extends Object> covariantList = aList;
List<? super String> contravariantList = aList;
Теперь вы можете получить доступ ко всем методам covariantList
, которые не принимают общий параметр (так как это должно быть что-то "extends Object" ), но getters будут работать нормально (поскольку возвращаемый объект всегда будет иметь тип "Object" )
Противоположно для contravariantList
: вы можете получить доступ ко всем методам с помощью общих параметров (вы знаете, что это должен быть суперкласс "String", поэтому вы всегда можете передать один), но без геттеров (возвращаемый тип может быть любого другого супертипа String)
Ответ 2
Со-дисперсия: Итератор и Итератор. Почти всегда имеет смысл определить ко-вариант Iterable
или Iterator
. Iterator<? extends T>
может использоваться так же, как Iterator<T>
- единственное место, где появляется параметр типа - это тип возвращаемого значения из метода next
, поэтому его можно безопасно поднять до T
. Но если у вас S
extends T
, вы можете также присвоить Iterator<S>
переменной типа Iterator<? extends T>
. Например, если вы определяете метод find:
boolean find(Iterable<Object> where, Object what)
вы не сможете называть его List<Integer>
и 5
, поэтому его лучше определить как
boolean find(Iterable<?> where, Object what)
Contra-variance: Comparator. Практически всегда имеет смысл использовать Comparator<? super T>
, потому что он может использоваться как Comparator<T>
. Параметр type отображается только как тип параметра метода compare
, поэтому T
можно безопасно передать ему. Например, если у вас есть DateComparator implements Comparator<java.util.Date> { ... }
, и вы хотите отсортировать List<java.sql.Date>
с этим компаратором (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)
Ответ 3
Посмотрите на принцип замены Лискова. Фактически, если класс B расширяет класс A, тогда вы должны иметь возможность использовать B, когда требуется A.