TL; DR, to jest błąd kompilatora.
Nie ma reguły, która dawałaby pierwszeństwo konkretnej stosownej metodzie po jej odziedziczeniu lub metodzie domyślnej. Co ciekawe, kiedy zmieniam kod na
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));
stwierdzenie wywołuje błąd w Eclipse.
Ponieważ żadna właściwość forEach(Consumer<? super T) c)
metody z Iterable<T>
interfejsu nie zmieniła się podczas deklarowania kolejnego przeciążenia, decyzja Eclipse o wybraniu tej metody nie może (konsekwentnie) opierać się na żadnej właściwości metody. Jest to wciąż jedyna metoda odziedziczona, wciąż jedyna default
metoda, wciąż jedyna metoda JDK i tak dalej. Żadna z tych właściwości i tak nie powinna wpływać na wybór metody.
Pamiętaj, że zmiana deklaracji na
interface CustomIterable<T> {
void forEach(ConsumerOne<? super T> c);
default void forEach(ConsumerTwo<? super T> c) {}
}
powoduje również „dwuznaczny” błąd, więc liczba stosowanych przeciążonych metod również nie ma znaczenia, nawet jeśli są tylko dwaj kandydaci, nie ma ogólnej preferencji względem default
metod.
Jak do tej pory wydaje się, że problem pojawia się, gdy istnieją dwie odpowiednie metody oraz default
metoda i relacja dziedziczenia, ale nie jest to właściwe miejsce na dalsze kopanie.
Ale zrozumiałe jest, że konstrukcje z twojego przykładu mogą być obsługiwane przez inny kod implementacyjny w kompilatorze, jeden wykazuje błąd, a drugi nie.
a -> aList.add(a)
jest niejawnie wpisanym wyrażeniem lambda, którego nie można użyć do rozwiązania problemu przeciążenia. W przeciwieństwie,(A a) -> aList.add(a)
jest wyraźnie wpisany wyrażeniem lambda, którego można użyć do wybrania pasującej metody spośród przeciążonych metod, ale to nie pomaga tutaj (nie powinno tutaj pomóc), ponieważ wszystkie metody mają typy parametrów z dokładnie taką samą sygnaturą funkcjonalną .
Jako kontrprzykład z
static void forEach(Consumer<String> c) {}
static void forEach(Predicate<String> c) {}
{
forEach(s -> s.isEmpty());
forEach((String s) -> s.isEmpty());
}
podpisy funkcjonalne różnią się, a użycie jawnie typu wyrażenia lambda może rzeczywiście pomóc w wyborze właściwej metody, podczas gdy niejawnie wpisane wyrażenie lambda nie pomaga, więc forEach(s -> s.isEmpty())
powoduje błąd kompilatora. I wszystkie kompilatory Java się z tym zgadzają.
Zauważ, że aList::add
jest to niejednoznaczne odwołanie do metody, ponieważ add
metoda jest również przeciążona, więc nie może również pomóc w wyborze metody, ale odwołania do metod mogą być przetwarzane przez inny kod. Przełączenie na jednoznacznym aList::contains
lub zmienia List
się Collection
, aby add
jednoznaczna, nie zmienił wynik w mojej instalacji Eclipse (kiedyś 2019-06
).