Відповіді:
Коваріація:
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:
List<String> aList...
List<? extends Object> covariantList = aList;
List<? super String> contravariantList = aList;
Тепер ви можете отримати доступ до всіх методів, covariantList
які не приймають загальний параметр (оскільки він повинен бути чимось "розширює об'єкт"), але getters буде працювати нормально (так як повернутий об'єкт завжди буде типу "Object")
Навпаки це стосується contravariantList
: Ви можете отримати доступ до всіх методів із загальними параметрами (ви знаєте, що це повинен бути надклас "String", тому ви завжди можете передавати його), але жодних отримувачів (тип, що повертається, може бути будь-якого іншого супертипу String )
Ко-дисперсія: Ітерабел та Ітератор. Це майже завжди можна буде визначити спільний варіант Iterable
або Iterator
. Iterator<? extends T>
може використовуватися так само Iterator<T>
- єдиним місцем, де з’являється параметр типу, є тип повернення з next
методу, тому його можна безпечно передати на T
. Але якщо у вас є S
розширення T
, ви також можете призначити Iterator<S>
змінну типу Iterator<? extends T>
. Наприклад, якщо ви визначаєте спосіб пошуку:
boolean find(Iterable<Object> where, Object what)
ви не зможете зателефонувати до цього List<Integer>
і 5
, тому його краще визначити як
boolean find(Iterable<?> where, Object what)
Контра-дисперсія: Компаратор. Це майже завжди має сенс використовувати Comparator<? super T>
, оскільки його можна використовувати так само Comparator<T>
. Параметр типу відображається лише як тип параметру 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)
Подивіться на принцип заміни Ліскова . Насправді, якщо клас B поширює клас A, то ви повинні мати можливість використовувати B, коли потрібен A.
contra variant
. super.doSomething("String")
не вдалося замінити на sub.doSomething(Object)
.