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


105

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

Ответы:


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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.