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