TL; DR, este é um bug do compilador.
Não há uma regra que daria precedência a um método aplicável específico quando ele é herdado ou um método padrão. Curiosamente, quando eu mudo o código para
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
}
a iterable.forEach((A a) -> aList.add(a));
instrução produz um erro no Eclipse.
Como nenhuma propriedade do forEach(Consumer<? super T) c)
método da Iterable<T>
interface foi alterada ao declarar outra sobrecarga, a decisão do Eclipse de selecionar esse método não pode ser (consistentemente) baseada em nenhuma propriedade do método. Ainda é o único método herdado, ainda é o único default
método, ainda é o único método JDK e assim por diante. Nenhuma dessas propriedades deve afetar a seleção do método de qualquer maneira.
Observe que alterar a declaração para
interface CustomIterable<T> {
void forEach(ConsumerOne<? super T> c);
default void forEach(ConsumerTwo<? super T> c) {}
}
também produz um erro "ambíguo"; portanto, o número de métodos sobrecarregados aplicáveis também não importa, mesmo quando há apenas dois candidatos, não há preferência geral pelos default
métodos.
Até agora, o problema parece surgir quando há dois métodos aplicáveis e um default
método e uma relação de herança estão envolvidos, mas esse não é o lugar certo para aprofundar.
Mas é compreensível que as construções do seu exemplo possam ser tratadas por diferentes códigos de implementação no compilador, um exibindo um bug enquanto o outro não.
a -> aList.add(a)
é uma expressão lambda de tipo implícito , que não pode ser usada para a resolução de sobrecarga. Por outro lado, (A a) -> aList.add(a)
é uma expressão lambda de tipo explícito que pode ser usada para selecionar um método correspondente dos métodos sobrecarregados, mas não ajuda aqui (não deve ajudar aqui), pois todos os métodos têm tipos de parâmetros com exatamente a mesma assinatura funcional .
Como um contra-exemplo, com
static void forEach(Consumer<String> c) {}
static void forEach(Predicate<String> c) {}
{
forEach(s -> s.isEmpty());
forEach((String s) -> s.isEmpty());
}
as assinaturas funcionais diferem e o uso de uma expressão lambda do tipo explicitamente pode realmente ajudar a selecionar o método certo, enquanto a expressão lambda implicitamente digitada não ajuda, portanto forEach(s -> s.isEmpty())
produz um erro do compilador. E todos os compiladores Java concordam com isso.
Observe que aList::add
é uma referência de método ambígua, pois o add
método também está sobrecarregado, por isso também não pode ajudar na seleção de um método, mas as referências a métodos podem ser processadas por código diferente de qualquer maneira. Mudar para um não ambíguo aList::contains
ou mudar List
para Collection
, para tornar add
não ambíguo, não alterou o resultado na minha instalação do Eclipse (eu usei 2019-06
).