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