Ух ты, это был забавный поиск.
Это зависит от точной спецификации, когда говорят, что метод переопределяет другой метод.
Соответствующая часть находится в §8.4.8.1 Переопределение (методами экземпляра) Java Спецификация языка (я использую один для Java 14).
Метод экземпляра mC
, объявленный или унаследованный классом C
, переопределяет C
другой метод mA
, объявленный в классе A
, если все следующее верно:
[...]
- Подпись m C является подписью (§8.4.2) подписи mA .
Итак, давайте посмотрим на §8.4.2. Подпись метода :
Два метода или конструктора, M
и N
, имеют одинаковую подпись, если имеют одинаковое имя, одинаковые параметры типа (если есть) (§8.4.4), и, после адаптации типов формальных параметров N
к параметрам типа M
, те же типы формальных параметров.
Подпись метода m1
подпись метода m2
, если либо:
m2
имеет такую же подпись, что и m1
, либо
подпись m1
совпадает с удаление (§4.6) подписи m2
.
Две сигнатуры метода m1
и m2
эквивалентны переопределению, если либо m1
является подписью m2
, либо m2
является подпись m1
.
В нашем случае следует отметить пару вещей:
Возвращаемое значение не является частью подписи, поэтому это в основном игнорируется при принятии решения о том, переопределяет ли один метод другой. Существуют дополнительные ограничения, основанные на типе возвращаемого значения, но эти ограничения не влияют, если два метода переопределяют друг друга, но если переопределение действительно компилируется. См. §8.4.8.3 Требования к переопределению и сокрытию .
Эти две подписи не являются одинаковыми потому что они не имеют одинакового количества параметров типа.
Они не являются подписями, потому что для этого требуется, чтобы одна была удалена другой, но обе подписи содержат общие c типы (List<String>
). Обратите внимание, что это меняется, если в сигнатурах метода нет универсальных выражений, т. Е. Если вы используете List
в качестве параметра или List<String>
появляется только в возвращаемом значении.
=> метод в Derived
не перекрывает метод в Base
.
Но их удаление такое же. Стирание в основном удаляет все параметры типа. См. §4.6 Тип Erasure для очевидно более сложных деталей. Но здесь важно то, что удаление подписей: void f(List arg)
Это нарушает раздел в §8.4.8.3 Требования по переопределению и сокрытию :
Ошибка времени компиляции, если объявление типа T имеет метод-член m1 и существует метод m2, объявленный в T, или супертип T, такой, что все перечисленное ниже истинно:
- m1 и m2 имеют одно и то же имя.
- m2 доступно (§6.6) из T.
- Подпись m1 не является подписью (§8.4.2 ) подписи m2.
- Подпись
m1
или некоторый метод m1 overrides (directly or indirectly)has the same erasure as the signature of
m2 or some method
m2` переопределяет (прямо или косвенно).
Конечно, это приводит нас к вопросу: почему используется странное определение subsignature
?
Это на самом деле объясняется в §8.4.2. Подпись метода :
Понятие подписи предназначено для express взаимосвязи между двумя методами, подписи которых не идентичны, но в которых один может переопределить другой , В частности, он позволяет методу, сигнатура которого не использует обобщенные типы c, переопределять любую обобщенную версию этого метода. Это важно, чтобы разработчики библиотеки могли свободно генерировать методы независимо от клиентов, которые определяют подклассы или подынтерфейсы библиотеки.