TL; DR, это ошибка компилятора.
Нет правила, которое бы отдавало приоритет конкретному применимому методу, когда он наследуется, или методу по умолчанию. Интересно, что когда я изменяю код на
interface ConsumerOne<T> {
void accept(T a);
}
interface ConsumerTwo<T> {
void accept(T a);
}
interface CustomIterable<T> extends Iterable<T> {
void forEach(ConsumerOne<? super T> c); //overload
void forEach(ConsumerTwo<? super T> c); //another overload
}
, оператор iterable.forEach((A a) -> aList.add(a));
выдает ошибку в Eclipse.
, поскольку ни одно свойство метода forEach(Consumer<? super T) c)
из интерфейса Iterable<T>
не изменилось при объявлении другой перегрузки решение Eclipse выбрать этот метод не может (последовательно) основываться на каком-либо свойстве метода. Это все еще единственный унаследованный метод, все еще единственный метод default
, все еще единственный метод JDK и так далее. В любом случае ни одно из этих свойств не должно влиять на выбор метода.
Обратите внимание, что изменение объявления на
interface CustomIterable<T> {
void forEach(ConsumerOne<? super T> c);
default void forEach(ConsumerTwo<? super T> c) {}
}
также приводит к «неоднозначной» ошибке, поэтому число применимых перегруженных методов не Также важно то, что даже когда есть только два кандидата, нет никакого общего предпочтения методам default
.
Пока что проблема возникает, когда есть два применимых метода и метод default
и отношения наследования вовлечены, но это не то место, чтобы копать дальше.
Но понятно, что конструкции вашего примера могут обрабатываться другим кодом реализации в компиляторе, один из которых показывает ошибку, пока другое - нет.
a -> aList.add(a)
- это неявно типизированное лямбда-выражение, которое нельзя использовать для разрешения перегрузки. Напротив, (A a) -> aList.add(a)
является явным образом лямбда-выражением, которое можно использовать для выбора подходящего метода из перегруженных методов, но здесь это не помогает (здесь не должно помогать), так как все методы иметь типы параметров с одинаковой функциональной сигнатурой.
В качестве контрпримера с
static void forEach(Consumer<String> c) {}
static void forEach(Predicate<String> c) {}
{
forEach(s -> s.isEmpty());
forEach((String s) -> s.isEmpty());
}
функциональные сигнатуры различаются, и использование лямбда-выражения явного типа действительно может помочь в выборе правильного метод, тогда как неявно типизированное лямбда-выражение не помогает, поэтому forEach(s -> s.isEmpty())
выдает ошибку компилятора. И все Java компиляторы согласны с этим.
Обратите внимание, что aList::add
является неоднозначной ссылкой на метод, так как метод add
также перегружен, поэтому он также не может не выбирать метод, но метод в любом случае ссылки могут обрабатываться другим кодом. Переключение на однозначный aList::contains
или изменение List
на Collection
, чтобы сделать add
однозначным, не изменило результат в моей установке Eclipse (я использовал 2019-06
).