Два метода с одним и тем же стиранием не являются необходимыми для переопределения-эквивалента (или они подписи не являются подписями между ними)?
Я читаю невероятную книгу "Руководство программиста по java scjp-сертификации" для jdk6, и есть раздел об универсальном переопределении. На нем описана подпись и эквивалентный переопределению и описаны некоторые примеры эквивалентного переопределения, которые я цитирую:
С учетом следующих трех обобщенных объявлений методов в классе:
static <T> void merge (MyStack<T> s1, MyStack<T> s2) { /*...*/ }
static <T> void merge (MyStack<T> s1, MyStack<? extends T> s2) { /*...*/ }
static <T> void merge (MyStack<T> s1, MyStack<? super T> s2) { /*...*/ }
После стирания подпись всех трех методов:
merge(MyStack, MyStack)
сигнатуры методов эквивалентны переопределению, следовательно, эти методы не перегружены.
Я не совсем согласен с тем, что эти методы эквивалентны переопределению, на самом деле я думаю, что у этих методов есть "конфликт имен при стирании", но ни один из них не является подписью другого… возможно, я неправ, поэтому я хочу немного осветить это.
Определения подписи заставляют меня думать, что они не являются подписями между ними.
В JSL 6 #8.4.2 Подпись метода ( http://docs.oracle.com/javase/specs/jls/se6/html/classes.html)
Два метода имеют одинаковую подпись, если они имеют одинаковые имена и типы аргументов. Два объявления метода или конструктора M и N имеют одинаковые типы аргументов, если выполняются все следующие условия:
O ни. имеют одинаковое количество формальных параметров (возможно, ноль)
Они имеют одинаковое количество параметров типа (возможно, ноль)
Позволять
<A1,...,An>
быть параметрами формального типа М и пусть<B1,...,Bn>
быть параметрами формального типа N. После переименования каждого вхождения Bi в типе N в Ai границы переменных соответствующего типа и типов аргументов M и N совпадают.Сигнатура метода m1 является подписями сигнатуры метода m2, если либо m2 имеет ту же сигнатуру, что и m1, либо сигнатура m1 совпадает с удалением сигнатуры m2
...
Две сигнатуры метода m1 и m2 эквивалентны переопределению, если либо m1 является подписью m2, либо m2 является подписью m1.
В JSL 8 # 8.4.2. Подпись метода ( http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html)
Два метода или конструктора, M и N, имеют одинаковую сигнатуру, если они имеют одинаковое имя, одинаковые параметры типа (если есть) (§8.4.4) и после адаптации формальных параметров типа N к параметрам типа М, те же формальные типы параметров.
Сигнатура метода m1 является подписью метода m2, если либо:
m2 имеет такую же подпись, как m1, или
подпись m1 такая же, как стирание подписи m2.
Две сигнатуры метода m1 и m2 эквивалентны переопределению, если либо m1 является подписью m2, либо m2 является подписью m1.
РЕДАКТИРОВАТЬ 1
Проще говоря, я сомневаюсь, что из определения подписи в отношении стирания я понимаю, что "одна подпись без стирания равна стиранию из другой подписи"... а не то, что "обе подписи после стирания равны".. его тонкий, но важный (кстати, определение переопределенного эквивалента основано на определении подписи, поэтому я спрашиваю в терминах подписей)
1 ответ
TL;DR
На мой взгляд, формулировка книги здесь не очень хорошо сочетается. Перегрузка определяется в терминах отрицания эквивалентности переопределения согласно JLS (8.4.9) (перефразируя: если два метода с одинаковым именем существуют, но не эквивалентны переопределению, они будут перегружены).
Но приведенный пример является примером, в котором методы НЕ эквивалентны переопределению, но вызывают ошибку времени компиляции по другим причинам (конфликт имен - конкретная ошибка времени компиляции, указанная в JLS 8.4.8.3) и, следовательно, не перегружаются.
преамбула
Насколько я понимаю, вы ставите вопрос о точной семантике этого предложения:
"... или подпись m1 совпадает с удалением подписи m2"
В комбинации с
m1 и m2 эквивалентны переопределению, если либо m1 является подписью m2, либо m2 является подписью m1.
Ваша книга подразумевает, что это следует интерпретировать как
"илистирание подписи m1 совпадает со стиранием подписи m2"
(добавлены слова жирным курсивом).
В то время как вы бы интерпретировали это как
"или подпись m1 (без стирания) такая же, как стирание подписи m2"
Ваша интерпретация верна. Я не думаю, что предложение является двусмысленным, поэтому я думаю, что интерпретировать его первым способом (то есть, чтостирание обеих подписей одинаково) неверно. Возможно, вы захотите взглянуть на этот связанный ответ, чтобы добавить вес к моему мнению здесь (я нашел его, так как тоже хотел проверить свое понимание).
Ответ (однако...)
Раздел книги, которую вы цитируете, на самом деле пытается описать перегрузку.
Теперь, когда мы думаем о перегрузке, JLS (8.4.9) говорит, что:
Если два метода класса (оба они объявлены в одном и том же классе или оба унаследованы классом или один объявлен и один унаследован) имеют одинаковое имя, но сигнатуры не эквивалентны переопределению, то имя метода называется перегружен.
Это было последовательным, по крайней мере, с Java 6. Это где связь междуoverride-equivalent
и перегрузка проистекает из.
Итак, ваши методы будут перегружены, потому что они не являются строго эквивалентными переопределению. Правильно?
Неправильно.
Поскольку чуть выше этого раздела в 8.4.8.3, JLS добавляет конкретную ошибку времени компиляции:
Это ошибка времени компиляции, если объявление типа T имеет метод-член m1 и существует метод m2, объявленный в T, или супертип T, такой, что все из следующего верно:
m1 и m2 имеют одинаковые названия.
м2 доступен с T.
Подпись m1 не является подписью (§8.4.2) подписи m2.
Сигнатура m1 или некоторого переопределения метода m1 (прямо или косвенно) имеет то же самое стирание, что и сигнатура m2 или переопределения некоторого метода m2 (прямо или косвенно).
Это сценарий в вашем примере. Чуть ниже этого раздела поясняется, почему это необходимо:
Эти ограничения необходимы, потому что генерики реализованы посредством стирания.Приведенное выше правило подразумевает, что методы, объявленные в одном и том же классе с одним и тем же именем, должны иметь разные стирания.Это также подразумевает, что объявление типа не может реализовывать или расширять два различных вызова одного и того же универсального интерфейса.
Примечание
Пример в книге странный, потому что Java не позволяет переопределять статические методы (скорее сигнатура метода в подклассе может скрыть это в суперклассе). Это делает концепцию того, чтобы не быть переопределенным эквивалентом, немного сложным для ученика, на мой взгляд. Тем не менее, вы можете удалить static
и до сих пор вижу эффект, который они пытаются продемонстрировать.