Почему контравариантные типы параметров в Java не разрешены для переопределения?

При переопределении метода суперкласса Java позволяет возвращаемому типу быть ковариантным.

Почему контравариантные типы параметров, напротив, не разрешены при переопределении методов?


person Will    schedule 15.09.2012    source источник
comment
возможный дубликат Почему нет параметра contra-variance для переопределения?   -  person Don Roby    schedule 15.09.2012
comment
Спасибо. Я видел это, но мне было трудно понять ответ на С++, так как я совершенно не знаком с С++. Подумал, что может быть проще специально попросить Java.   -  person Will    schedule 15.09.2012
comment
Я прочитал связанный пост и, насколько я понимаю, выгода от этой функции не перевешивает повышенный фактор неожиданности. И не так уж сложно предоставить перегруженный метод для конкретной реализации. Я имею в виду, какие другие варианты использования этой функции вы видите? кроме прямого вызова метода в классе?   -  person Ivan Koblik    schedule 15.09.2012


Ответы (1)


Потому что это называется перегрузкой.

В частности, тип возвращаемого типа может быть ковариантным, поскольку он не учитывается при перегрузке и, следовательно, по-прежнему соответствует суперклассу или реализации интерфейса. Параметры учитываются при перегрузке. У вас вполне может быть оптимизация с Number doSomethingWithNumber(Integer value) по сравнению с Number doSomethingWithNumber(Number value).

person pickypg    schedule 15.09.2012
comment
Предполагая, что контравариантные параметры по-прежнему будут переопределять, с какими проблемами мы столкнемся? Я ищу что-то вроде: stackoverflow.com/a/2996901/715236, однако что плохого в том, чтобы иметь метод в подклассе переопределяет несколько в суперклассе? - person Will; 15.09.2012
comment
Самой большой проблемой будет невозможность обеспечить перегрузки, если где-то в цепочке задана какая-либо перегрузка базового типа. Это привело бы к хаосу с вызовами полиморфных функций, поскольку поиск должен был бы измениться с простой эквивалентной подписи на эквивалентную или более простую подпись, и что вы должны вызывать, если у вас есть обе, но вызывать более конкретную? С точки зрения дизайна, если A extends B и B extends C, но B обеспечивают реализацию void f(Object o) из вашей ссылки, то A не имеет возможности реализовать более конкретные перегруженные методы. Это был бы кошмар. - person pickypg; 15.09.2012
comment
Самое главное, я думаю, что это привело бы к ленивому переопределению API, что привело бы к большому количеству невероятно уродливого шаблонного кода, который внутренне использовался бы для вызова соответствующего метода: void f(Object o) { if (o instanceof Integer) f((Integer)o); }. И тогда вы должны спросить, как это вообще работает? Чтобы разрешить поведение переопределения, это означало бы, что метод параметра Object переопределяет метод параметра Integer, и, следовательно, приведенный выше код вызовет бесконечный рекурсивный цикл (пока стек не переполнится), поскольку он вернется к методу Object. - person pickypg; 15.09.2012
comment
Если только вы не используете версию Object для простого вызова других методов (не переопределений) или реализаций в классе super, что даст конкретный пункт назначения, отличный от контравариантного переопределения. Это означает, что код будет невероятно ограниченным, и я ожидаю, что количество ошибок здесь будет невероятно ограниченным. Не говоря уже о том, что вы теряете функциональность перегрузки, имея универсальную корзину, которая может появиться за пределами исходного API (в конце концов, сегодня вы могли бы перегрузить эти параметры в базовом классе, чтобы получить желаемое поведение, используя перегрузку и Object). - person pickypg; 15.09.2012
comment
@pickypg : Because that's called overloading. : я чуть со стула не упал, читая это. Спасибо за этот яркий ответ. +1 - person paercebal; 06.06.2013