Это правило предназначено для предотвращения конфликтов в устаревшем коде, который все еще использует необработанные типы.
Вот иллюстрация того, почему это было запрещено, взято из JLS. Предположим, что до того, как дженерики были представлены в Java, я написал такой код:
class CollectionConverter {
List toList(Collection c) {...}
}
Вы расширяете мой класс, вот так:
class Overrider extends CollectionConverter{
List toList(Collection c) {...}
}
После введения дженериков я решил обновить свою библиотеку.
class CollectionConverter {
<T> List<T> toList(Collection<T> c) {...}
}
Вы не готовы делать какие-либо обновления, поэтому вы оставляете свой Overrider
класс в покое. Чтобы правильно переопределить toList()
метод, разработчики языка решили, что необработанный тип «эквивалентен переопределению» для любого обобщенного типа. Это означает, что хотя подпись вашего метода формально больше не равна подписи моего суперкласса, ваш метод все равно переопределяет.
Сейчас проходит время, и вы решаете, что готовы обновить свой класс. Но вы немного облажались, и вместо редактирования существующего необработанного toList()
метода вы добавили новый метод, подобный этому:
class Overrider extends CollectionConverter {
@Override
List toList(Collection c) {...}
@Override
<T> List<T> toList(Collection<T> c) {...}
}
Из-за переопределенной эквивалентности необработанных типов оба метода имеют допустимую форму для переопределения toList(Collection<T>)
метода. Но, конечно, компилятор должен разрешить один метод. Чтобы устранить эту неоднозначность, классам не разрешается иметь несколько методов, эквивалентных переопределению, то есть несколько методов с одинаковыми типами параметров после стирания.
Ключ заключается в том, что это правило языка, разработанное для обеспечения совместимости со старым кодом с использованием необработанных типов. Это не ограничение, требуемое стиранием параметров типа; поскольку разрешение метода происходит во время компиляции, было бы достаточно добавить универсальные типы к идентификатору метода.