Интересно, почему это корректное переопределение:
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) подписи mA.
- ...
Подписи определены в JLS §8.4.2 :
Два метода или конструктора, M и N, имеют одинаковую сигнатуру , если они имеют одинаковое имя, одинаковые параметры типа (если есть) (§8.4.4) и после адаптации формального параметры типа N для параметров типа M, те же формальные параметры типа.
Подпись метода m1 является подписью метода m2, если либо:
- м2 имеет ту же подпись, что и m1, или
- подпись m1 совпадает с удалением (§4.6) подписи m2.
Похоже, что B.getSupplier
является подписью A.getSupplier
, но B.getSuppliers
является , а не подписью A.getSuppliers
.
Интересно, как это может быть.
Если B.getSupplier
является подписью A.getSupplier
, поскольку оно имеет такое же стирание, то B.getSuppliers
также должно иметь такое же стирание, что и A.getSuppliers
. Этого должно быть достаточно, чтобы переопределение getSuppliers
было законным, но это не так.
Если B.getSupplier
является подписью A.getSupplier
, поскольку она имеет одинаковую подпись, то мне интересно, что именно означает "параметры одного типа (если есть)".
Если рассматриваются параметры типа, то они должны иметь разные параметры типа: A.getSupplier
имеет параметр типа X
, B.getSupplier
не имеет ни одного.
Если параметры типа не учитываются, тогда чем же отличается getSuppliers
? 1069 *
Это скорее академический вопрос о переопределениях и обобщениях, поэтому, пожалуйста, не предлагайте рефакторинг кода (например, перенос параметра типа X
в класс и т. Д.).
Я ищу официальный ответ на основе JLS.
С моей точки зрения, B.getSupplier
не должно быть в состоянии переопределить A.getSupplier
, так как они не имеют одинаковые параметры типа. Это делает следующий код (который выдает ClassCastException
) легальным:
A b = new B();
URL url = b.<URL>getSupplier().get();