Столкновение имен при переопределении метода универсального класса

Я пытаюсь понять, что произошла ошибка в конфликте имен со следующим кодом:

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) {
}

Дальнейшее чтение:

Другие вопросы по тегам