Интересно, почему это допустимое переопределение:
public abstract class A {
public abstract <X> Supplier<X> getSupplier();
public static class B extends A {
@Override
public Supplier<String> getSupplier() {
return String::new;
}
}
}
В то время как это не так:
public abstract class A {
public abstract <X> Supplier<X> getSuppliers(Collection<String> strings);
public static class B extends A {
@Override
public Supplier<String> getSuppliers(Collection<String> strings) {
return String::new;
}
}
}
Согласно JLS §8.4.8.1, B.getSupplier
должен быть подъявлением A.getSupplier
:
Метод экземпляра mC, объявленный или унаследованный классом C, переопределяет из C другой метод mA, объявленный в классе A, если все это верно:
- ...
- Подпись mC является подзаконной (§8.4.2) сигнатуры мА.
- ...
Подписи определены в JLS §8.4.2:
Два метода или конструкторы M и N имеют одну и ту же подпись, если они имеют одинаковое имя, те же параметры типа (если они есть) (§8.4.4) и после адаптации формальных типов параметров N к параметрам типа из M, те же формальные типы параметров.
Подпись метода m1 является подширкой сигнатуры метода m2, если либо:
- m2 имеет ту же подпись, что и m1, или
- подпись m1 такая же, как стирание (§4.6) подписи m2.
Таким образом, похоже, что B.getSupplier
- подглавность A.getSupplier
но B.getSuppliers
не является подъявлением A.getSuppliers
.
Интересно, как это может быть.
Если B.getSupplier
является подъявлением A.getSupplier
поскольку он имеет такое же стирание, то B.getSuppliers
также должен иметь такое же стирание, как A.getSuppliers
. Этого должно быть достаточно для getSuppliers
чтобы переопределить getSuppliers
чтобы быть законным, но это не так.
Если B.getSupplier
является подъявлением A.getSupplier
потому что он имеет одну и ту же подпись, тогда я задаюсь вопросом, что именно означает "параметры одного и того же типа (если они есть)".
Если параметры типа считаются, то они должны иметь разные параметры типа: A.getSupplier
имеет параметр типа X
, B.getSupplier
имеет.
Если параметры типа не учитываются, то как getSuppliers
отличаются?
Это скорее академический вопрос об переопределениях и дженериках, поэтому, пожалуйста, не предлагайте рефакторинг кода (например, перемещение параметра типа X
в класс и т.д.).
Я ищу официальный, основанный на JLS ответ.
С моей точки зрения, B.getSupplier
не должен переопределять A.getSupplier
поскольку они не имеют одинаковых параметров типа. Это делает следующий код (который производит ClassCastException
) законным:
A b = new B();
URL url = b.<URL>getSupplier().get();