TL; DR, questo è un bug del compilatore.
Non esiste una regola che dia la precedenza a un particolare metodo applicabile quando viene ereditato o un metodo predefinito. È interessante notare che quando cambio il codice in
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
}
l' iterable.forEach((A a) -> aList.add(a));
istruzione produce un errore in Eclipse.
Poiché nessuna proprietà del forEach(Consumer<? super T) c)
metodo Iterable<T>
dall'interfaccia è cambiata quando si dichiara un altro sovraccarico, la decisione di Eclipse di selezionare questo metodo non può essere (coerentemente) basata su alcuna proprietà del metodo. È ancora l'unico metodo ereditato, ancora l'unico default
metodo, ancora l'unico metodo JDK e così via. Nessuna di queste proprietà dovrebbe comunque influire sulla selezione del metodo.
Si noti che cambiando la dichiarazione in
interface CustomIterable<T> {
void forEach(ConsumerOne<? super T> c);
default void forEach(ConsumerTwo<? super T> c) {}
}
produce anche un errore "ambiguo", quindi anche il numero di metodi sovraccarichi applicabili non ha importanza, anche quando ci sono solo due candidati, non esiste una preferenza generale verso i default
metodi.
Finora, il problema sembra apparire quando ci sono due metodi applicabili e default
sono coinvolti un metodo e una relazione di ereditarietà, ma questo non è il posto giusto per approfondire.
Ma è comprensibile che i costrutti del tuo esempio possano essere gestiti da diversi codici di implementazione nel compilatore, uno che mostra un bug mentre l'altro no.
a -> aList.add(a)
è un'espressione lambda tipizzata in modo implicito , che non può essere utilizzata per la risoluzione del sovraccarico. Al contrario, (A a) -> aList.add(a)
è un un'espressione lambda tipizzata in modo esplicito che può essere utilizzata per selezionare un metodo corrispondente dai metodi sovraccarichi, ma non aiuta qui (non dovrebbe aiutare qui), poiché tutti i metodi hanno tipi di parametri con esattamente la stessa firma funzionale .
Come controesempio, con
static void forEach(Consumer<String> c) {}
static void forEach(Predicate<String> c) {}
{
forEach(s -> s.isEmpty());
forEach((String s) -> s.isEmpty());
}
le firme funzionali differiscono e l'uso di un'espressione lambda di tipo esplicito può effettivamente aiutare a selezionare il metodo giusto mentre l'espressione lambda tipicamente implicita non aiuta, quindi forEach(s -> s.isEmpty())
produce un errore del compilatore. E tutti i compilatori Java sono d'accordo su questo.
Si noti che si aList::add
tratta di un riferimento al metodo ambiguo, poiché anche il add
metodo è sovraccarico, quindi non può essere di aiuto nella selezione di un metodo, ma i riferimenti al metodo potrebbero comunque essere elaborati da un codice diverso. Passare a un non ambiguo aList::contains
o passare List
a Collection
, per renderlo add
non ambiguo, non ha modificato il risultato nella mia installazione di Eclipse (ho usato 2019-06
).