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
).