I was answering a question and ran into a scenario I can\'t explain. Consider this code:
interface ConsumerOne {
void accept(T a);
}
interface Cust
TL;DR, this is a compiler bug.
There is no rule that would give precedence to a particular applicable method when it is inherited or a default method. Interestingly, when I change the code to
interface ConsumerOne {
void accept(T a);
}
interface ConsumerTwo {
void accept(T a);
}
interface CustomIterable extends Iterable {
void forEach(ConsumerOne super T> c); //overload
void forEach(ConsumerTwo super T> c); //another overload
}
the iterable.forEach((A a) -> aList.add(a));
statement produces an error in Eclipse.
Since no property of the forEach(Consumer super T) c)
method from the Iterable
interface changed when declaring another overload, Eclipse’s decision to select this method can not be (consistently) based on any property of the method. It’s still the only inherited method, still the only default
method, still the only JDK method, and so on. Neither of these properties should affect the method selection anyway.
Note that changing the declaration to
interface CustomIterable {
void forEach(ConsumerOne super T> c);
default void forEach(ConsumerTwo super T> c) {}
}
also produces an “ambiguous” error, so the number of applicable overloaded methods doesn’t matter either, even when there are only two candidates, there is no general preference towards default
methods.
So far, the issue seems to appear when there are two applicable methods and a default
method and an inheritance relationship are involved, but this is not the right place to dig further.
But it’s understandable that the constructs of your example may be handled by different implementation code in the compiler, one exhibiting a bug while the other doesn’t.
a -> aList.add(a)
is an implicitly typed lambda expression, which can’t be used for the overload resolution. In contrast, (A a) -> aList.add(a)
is an explicitly typed lambda expression which can be used to select a matching method from the overloaded methods, but it doesn’t help here (shouldn’t help here), as all methods have parameter types with exactly the same functional signature.
As a counter-example, with
static void forEach(Consumer c) {}
static void forEach(Predicate c) {}
{
forEach(s -> s.isEmpty());
forEach((String s) -> s.isEmpty());
}
the functional signatures differ, and using an explicitly type lambda expression can indeed help selecting the right method whereas the implicitly typed lambda expression doesn’t help, so forEach(s -> s.isEmpty())
produces a compiler error. And all Java compilers agree about that.
Note that aList::add
is an ambiguous method reference, as the add
method is overloaded too, so it also can’t help selecting a method, but method references might get processed by different code anyway. Switching to an unambiguous aList::contains
or changing List
to Collection
, to make add
unambiguous, did not change the outcome in my Eclipse installation (I used 2019-06
).