Ответ 1
Пока первый пример компилируется, он выдаст предупреждение о непроверенной конверсии:
// Type safety: The return type List<?> for foo() from the type C needs
// unchecked conversion to conform to List<String>
public List<?> foo()
{
return null;
}
Здесь происходит то, что объявляя параметры типа, A.foo()
и B.foo()
являются общие методы. Тогда переопределение C.foo()
опускает этот параметр типа. Это похоже на использование необработанного типа, по сути, "отказ" от проверки общего типа для этой сигнатуры метода. Это заставляет компилятор использовать унаследованные методы erasures: List<String> foo()
и List<Integer> foo()
оба становятся List foo()
, что может поэтому быть реализовано C.foo()
.
Вы можете видеть, что, сохраняя параметр type в объявлении C.foo()
, вместо этого будет ожидаемая ошибка компилятора:
// The return type is incompatible with A.foo()
public <T> List<?> foo()
{
return null;
}
Аналогично, если какой-либо из методов интерфейса не объявляет параметр типа, то исключение параметра типа из переопределения не позволяет "отказаться" от проверки общего типа для этого метода, а тип возврата List<?>
остается несовместимым.
Это поведение описано в JLS §8.4.2:
Понятие поднаборки предназначено для выражения взаимосвязи между двумя методами, чьи подписи не идентичны, но в которых можно переопределить другую. В частности, он позволяет методу, подпись которого не использует общие типы, чтобы переопределить любую генерализованную версию этого метода. Это важно, так что разработчики библиотек могут свободно генерировать методы независимо от клиентов, которые определяют подклассы или подинтерфейсы библиотеки.
Часто задаваемые вопросы о генералитетах Angelika Langer расширяются по этому поведению в ее разделе Может ли универсальный метод переопределить общий?:
Теперь рассмотрим пример, в котором не общие методы подтипа переопределить общие методы супертипа. Не общие методы подтипа считаются переопределяющими версиями общих методов супертипа, если стирания подписей идентичны.
Пример (из не общих методов подтипа, переопределяющих общий супертип методы):
class Super { public <T> void set( T arg) { ... } public <T> T get() { ... } } class Sub extends Super { public void set( Object arg) { ... } // overrides public Object get() { ... } // overrides with unchecked warning }
warning: get() in Sub overrides <T>get() in Super; return type requires unchecked conversion found : Object required: T public Object get() {
Здесь методы подтипа имеют подписи, а именно
set(Object)
иget()
, которые идентичны стиранию методов супертипа. Эти титрованные подписи считаются эквивалентными.В случае метода
get
существует один недостаток: мы получаем unchecked warning, потому что типы возврата на самом деле не совместимы. Тип возвращаемого метода подтипаget
равенObject
, тип возврата метода супертипа get - параметр неограниченного типа. тип возврата метода подтипа не идентичен супертипу тип возвращаемого метода и не является его подтипом; в обеих ситуациях компилятор с радостью примет типы возврата как совместимые. Вместо этого возвращаемый тип метода подтипаObject
можно преобразовать в тип возвращаемого типа супертипа посредством непроверенного преобразования. Непроверенное предупреждение указывает на необходимость проверки типа, которая ни компилятор, ни виртуальная машина не могут выполнять. В других слов, неконтролируемая операция не безопасна для типов. В случае конвертируемых типов возврата, кто-то должен был убедиться, что Возвращаемое значение метода подтипа является совместимым с типом супертипа метод возвращаемого типа, но никто, кроме программиста, не может обеспечить это.