Столкновение имен при переопределении метода универсального класса
Я пытаюсь понять, что произошла ошибка в конфликте имен со следующим кодом:
import java.util.*;
import javax.swing.*;
class Foo<R extends Number> {
public void doSomething(Number n, Map<String, JComponent> comps) {
}
}
class Bar extends Foo {
public void doSomething(Number n, Map<String, JComponent> comps) {
}
}
Сообщение об ошибке:
ошибка: имя конфликтует:
doSomething(Number,Map<String,JComponent>)
вBar
а такжеdoSomething(Number,Map<String,JComponent>)
вFoo
иметь то же самое стирание, но ни один не перекрывает другой
Я знаю, что могу исправить это, удалив общий тип из Foo
или путем изменения Bar
декларация class Bar extends Foo<Integer>
; я хочу знать, почему эта ошибка возникает в конкретном случае, но исчезает, если я удаляю comps
параметр от каждого метода. Я немного читал об удалении типов, но мне все еще кажется, что оба метода должны иметь одинаковое стирание с универсальными шаблонами или без них, и, следовательно, должны быть корректными переопределениями в любом случае. (Обратите внимание, что я еще нигде не использовал универсальный параметр, поэтому я так удивлен.)
Я знаю, что раньше я добавлял универсальные типы в родительские классы, но получал только предупреждения о подклассах, а не об ошибках. Кто-нибудь может объяснить этот сценарий?
1 ответ
Луиджи прямо в комментариях. Это следствие необработанных типов.
Супертип класса может быть необработанным типом. Доступ к элементам для класса рассматривается как нормальный, а доступ к элементам для супертипа обрабатывается как для необработанных типов. В конструкторе класса вызовы super обрабатываются как вызовы методов необработанного типа.
Это применяется при вызове метода супертипа, но также и при переопределении.
Взять, к примеру, следующее
class Bar extends Foo {
public Bar() {
doSomething(1, new HashMap<Number, String>());
}
}
Вы заметите, что он компилируется, даже если HashMap<Number, String>
это не тип, который может быть назначен Map<String, JComponent>
,
Тип конструктора (§8.8), метод экземпляра (§8.4, §9.4) или нестатическое поле (§8.3) необработанного типа
C
который не унаследован от своих суперклассов или суперинтерфейсов, является необработанным типом, который соответствует стиранию его типа в общем объявлении, соответствующемC
,
(Обратите внимание, что C
в нашем случае Bar
.)
И то же самое происходит при попытке переопределить метод. При попытке переопределить Foo#doSomething(..)
метод, ваш Bar
класс на самом деле видит это объявлено
public void doSomething(Number n, Map comps) {
}
Другими словами, каждое использование параметров типа стирается. Таким образом, пытаясь объявить метод
public void doSomething(Number n, Map<String, JComponent> comps) {
}
в подтипе Bar
на самом деле попытка перегрузки, а не переопределения. И это не удается из-за стирания типа. Правильное переопределение, которое вы можете проверить с помощью @Override
, является
public void doSomething(Number n, Map comps) {
}
Дальнейшее чтение: