Приведите примеры функций, которые демонстрируют ковариацию и контравариантность в случаях как перегрузки, так и переопределения в Java? [закрыто]

105

Пожалуйста, покажите хороший пример ковариации и контравариантности в Java.

JavaUser
источник

Ответы:

155

Ковариация:

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 (). Другие языки поддерживают этот стиль контравариантности.

Дженерики

Это также возможно для дженериков:

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

Теперь вы можете получить доступ ко всем методам, covariantListкоторые не принимают общий параметр (поскольку он должен быть что-то «расширяет объект»), но геттеры будут работать нормально (поскольку возвращаемый объект всегда будет иметь тип «Object»)

Обратное верно для contravariantList: вы можете получить доступ ко всем методам с общими параметрами (вы знаете, что это должен быть суперкласс String, поэтому вы всегда можете передать его), но без геттеров (возвращаемый тип может быть любого другого супертипа String )

Жестко запрограммированный
источник
79
Первый пример контравариантности не работает в Java. doSomething () в классе Sub - это перегрузка, а не переопределение.
Крейг П. Мотлин
15
На самом деле. Java не поддерживает контравариантные аргументы при выделении подтипов. Только ковариация для возвращаемых типов методов (как в первом примере).
the_dark_destructor
Отличный ответ. Мне кажется, что ковариация логична. Но не могли бы вы указать мне на абзац в JLS, который описывает контравариантность? Почему вызывается Sub.doSomething?
Михаил
2
Как заметил Крейг, это не так. Я думаю, здесь происходит столкновение между переопределением и перегрузкой, и SUN выбрал (как всегда) вариант с обратной совместимостью. Таким образом, в Java вы не можете использовать контравариантные параметры при переопределении метода.
Hardcoded
1
Было бы неплохо узнать, почему я получаю отрицательные голоса за свой ответ.
Жестко запрограммировано
48

Ковариация: Iterable и Iterator. Почти всегда имеет смысл определить ковариант 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)
Ярдена
источник
-4

Посмотрите на принцип подстановки Лискова . Фактически, если класс B расширяет класс A, вы должны иметь возможность использовать B всякий раз, когда требуется A.

посторонний
источник
6
Это не ответ на вопрос и вводит в заблуждение. Было бы вполне возможно разработать вариантную систему, которая нарушает семантическую корректность и, следовательно, нарушает LSP.
Мэтт Уиппл
это не тот случай, чтобы contra variantсказать. super.doSomething("String")не может быть заменен на sub.doSomething(Object).
zinking
Это не вопрос
OlivierTerrien